chaims / study

to do study
0 stars 0 forks source link

webpack #12

Open chaims opened 7 years ago

chaims commented 7 years ago

目录

目标:

将依赖树拆分,保证按需加载
保证初始加载的速度
所有静态资源可以被模块化
可以整合第三方的库和模块
可以构造大系统

安装

$ npm install webpack -g

当然如果常规项目还是把依赖写入 package.json 包去更人性化:

$ npm init
$ npm install webpack --save-dev
$ npm install webpack@1.2.x --save-dev
$ npm install webpack@1.14 -S  //-save简写-S  -dev简写-D
chaims commented 7 years ago

工作原理

  1. 从 context 对应的文件夹开始……寻找 entry 里所有的文件名……然后读取它们的内容。
  2. 在解析代码时,每一个通过 import(ES6) 或 require()(Node) 引入的依赖都会被打包到最终的构建结果当中。它会接着搜索那些依赖,以及那些依赖的依赖,直到“依赖树”的叶子节点 — 只打包它所需要的依赖,没有其他的东西。
  3. 接着,Webpack 将所有东西打包到 output.path 对应的文件夹里,使用 output.filename 对应的命名模板来命名([name] 被 entry 里的对象键值所替代)

Example

const path = require('path');
const webpack = require('webpack');
module.exports = {
  context: path.resolve(__dirname, './src'),
  entry: {
    app: ['./home.js', './events.js', './vendor.js'],
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js',
  },
};
chaims commented 6 years ago

核心概念

module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' } };

- output.filename:  webpack bundle 的名称
- output.path:  bundle 生成(emit)到哪里

### loader
loader 能够 import 导入任何类型的模块(例如 .css 文件),并且先将它们转换为有效 模块,然后添加到依赖图中。

const path = require('path'); module.exports = { output: { filename: 'my-first-webpack.bundle.js' }, module: { rules: [ { test: /.txt$/, use: 'raw-loader' } ] } };

- test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
- use 属性,表示进行转换时,应该使用哪个 loader。
> [loader](https://webpack.docschina.org/concepts/loaders)
### 插件(plugins)
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务,插件的范围包括:打包优化、资源管理和注入环境变量。
想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装 const webpack = require('webpack'); // 用于访问内置插件

module.exports = { module: { rules: [ { test: /.txt$/, use: 'raw-loader' } ] }, plugins: [ new HtmlWebpackPlugin({template: './src/index.html'}) ] };

> [插件列表](https://webpack.docschina.org/plugins)

### 模式
通过将```mode```参数设置为```development, production 或 none```,可以启用对应环境下 webpack 内置的优化。默认值为```production```。

module.exports = { mode: 'production' };

或者

webpack --mode=production


### 资料
- [手动打包一个应用程序](https://www.youtube.com/watch?v=UNMkLHzofQI)
- [实时编写一个简单的模块打包工具](https://www.youtube.com/watch?v=Gc9-7PBqOC8)
- [简单模块打包工具的细节说明](https://github.com/ronami/minipack)
chaims commented 6 years ago

指南

起步

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev

使用一个配置文件

  webpack-demo
  |- package.json
+ |- webpack.config.js
  |- /dist
    |- index.html
  |- /src
    |- index.js

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
};
执行
npx webpack --config webpack.config.js

NPM 脚本

  {
    "name": "webpack-demo",
    "version": "1.0.0",
    "description": "",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
+     "build": "webpack"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
      "webpack": "^4.0.1",
      "webpack-cli": "^2.0.9",
      "lodash": "^4.17.5"
    }
  }
执行
npm run build

管理资源

加载 CSS

在 module 配置中 安装并添加 style-loader 和 css-loader

npm install --save-dev style-loader css-loader

webpack.config.js

+   module: {
+     rules: [
+       {
+         test: /\.css$/,
+         use: [
+           'style-loader',
+           'css-loader'
+         ]
+       }
+     ]
+   }

index.js

import './style.css';
......

加载图片

使用 file-loader,我们可以轻松地将这些内容混合到 CSS 中:

npm install --save-dev file-loader

webpack.config.js

+       {
+         test: /\.(png|svg|jpg|gif)$/,
+         use: [
+           'file-loader'
+         ]
+       }

当使用 css-loader 时,CSS 中的 url('./my-image.png') 会使用类似的过程去处理; html-loader 以相同的方式处理 <img src="./my-image.png" />

压缩和优化图像查看 image-webpack-loader 和 url-loader 了解更多关于增强加载处理图片功能。

加载字体

file-loader 和 url-loader 可以接收并加载任何文件,然后将其输出到构建目录,同图片处理

加载数据

加载的有用资源还有数据,如 JSON 文件,CSV、TSV 和 XML,要导入 CSV、TSV 和 XML,你可以使用 csv-loaderxml-loader

全局资源

以以上的方式加载资源,你可以以更直观的方式将模块和资源组合在一起,使你的代码更具备可移植性,类似下面的结构:

- |- /assets
+ |– /components
+ |  |– /my-component
+ |  |  |– index.jsx
+ |  |  |– index.css
+ |  |  |– icon.svg
+ |  |  |– img.png

管理输出

见上面 output章节

+   entry: {
+     app: './src/index.js',
+     print: './src/print.js'
+   },
output: {
+     filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}

HtmlWebpackPlugin

HtmlWebpackPlugin 默认生成 index.html 文件

+   plugins: [
+     new HtmlWebpackPlugin({
+       title: 'Output Management'
+     })
+   ],

HtmlWebpackPlugin

清理 /dist 文件夹

new CleanWebpackPlugin(['dist'])

Manifest

TODO

开发

source map

为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。 source map 有很多不同的选项可用:

2018-11-08 6 41 36

选择一个开发工具

webpack 中有几个不同的选项,可以帮助你在代码发生变化后自动编译代码:

模块热替换(Hot Module Replacement)

HMR 不适用于生产环境,应当只在开发环境使用

启用 HMR

+ const webpack = require('webpack');
devServer: {
contentBase: './dist',
+     hot: true
},
plugins: [
...
+     new webpack.HotModuleReplacementPlugin()
],

当使用 webpack dev server 和 Node.js API 时,不要将 dev server 选项放在 webpack 配置对象中,而是,在创建选项时,将其作为第二个参数传递


new WebpackDevServer(compiler, options)

addDevServerEntrypoints

chaims commented 6 years ago

目录

tree shaking

移除 JavaScript 上下文中的未引用代码。

side-effect-free

将文件标记为无副作用,简单地将该属性标记为 false,来告知 webpack,它可以安全地删除未用到的 export 导出。 pack.json

{
  "name": "your-project",
  "sideEffects": false
}
{
  "name": "your-project",
  "sideEffects": [
    "./src/some-side-effectful-file.js",
    "*.css"
  ]
}

压缩

从 webpack 4 开始,也可以通过 "mode" 配置选项轻松切换到压缩输出,只需设置为 "production"。 也可以在命令行接口中使用 --optimize-minimize标记,来启用 UglifyJSPlugin。

使用 tree shaking,你必须:

使用 ES2015 模块语法(即 import 和 export)。
在项目 package.json 文件中,添加一个 "sideEffects" 属性。
引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。

生产环境构建

配置

通常建议为每个环境编写彼此独立的 webpack 配置。 webpack-merge

+ const merge = require('webpack-merge');
+ const common = require('./webpack.common.js');
+
+ module.exports = merge(common, {
+   mode: 'production',
+ });

Minification

src/index.js

 import { cube } from './math.js';
+
+ if (process.env.NODE_ENV !== 'production') {
+   console.log('Looks like we are in development mode!');
+ }

压缩 CSS

CLI 替代选项

--optimize-minimize标记将在后台引用 UglifyJSPlugin

--define process.env.NODE_ENV="'production'"
chaims commented 6 years ago

代码分离

把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件,有以下几种方法:

防止重复(prevent duplication)

SplitChunksPlugin SplitChunks 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。

+   optimization: {
+     splitChunks: {
+       chunks: 'all'
+     }
+   }

一些对于代码分离很有帮助的插件和 loaders

mini-css-extract-plugin: 用于将 CSS 从主应用程序中分离。
bundle-loader: 用于分离代码和延迟加载生成的 bundle。
promise-loader: 类似于 bundle-loader ,但是使用的是 promises。

动态导入(dynamic imports)

当涉及到动态代码拆分时,webpack 提供了两个类似的技术:

chaims commented 6 years ago

创建 library

创建一个 library

基本配置

打包library,能够实现以下几个目标:

此外,用户应该能够通过以下方式访问 library:

外部化 lodash

更倾向于把 lodash 当作 peerDependency,放弃对外部 library 的控制,而是将控制权让给使用 library 的用户

+   externals: {
+     lodash: {
+       commonjs: 'lodash',
+       commonjs2: 'lodash',
+       amd: 'lodash',
+       root: '_'
+     }
+   }

外部扩展的限制

对于从一个依赖目录中,调用多个文件的 library,需要逐个排除它们,或者使用正则表达式排除:

externals: [
    'library/one',
    'library/two',
    // 所有以 "library/" 开始的
    /^library\/.+$/
]

暴露 library

我们希望能够兼容不同的环境,例如 CommonJS,AMD,Node.js 或者作为一个全局变量。为了让 library 能够在各种用户环境(consumption)中可用,需要在 output 中添加 library 属性:

output: {
      path: path.resolve(__dirname, 'dist'),
+     filename: 'webpack-numbers.js',
+     library: 'webpackNumbers',
+     libraryTarget: 'umd'
 },

当 import 引入模块时,可以将 library bundle 暴露为名为 webpackNumbers 的全局变量。为了让 library 和其他环境兼容,还需要在配置文件中添加 libraryTarget 属性。这是可以控制 library 如何以不同方式暴露的选项。

暴露 library的方式:

如果设置了 library 但没设置 libraryTarget,则 libraryTarget 默认为 var

最终步骤

通过设置 package.json 中的 main 字段,添加生成 bundle 的文件路径:

{
...
"main": "dist/webpack-numbers.js",
...
}

{
...
"module": "src/index.js",
...
}