阅读 3292

webpack从入门到放弃

webpack构建流程

从启动webpack构建到输出结果经历了一系列过程:

  1. 解析webpack.config.js配置参数,调用shell并追加命令行参数,通过 optimist将前两者参数整合成 options 对象传到了下一个流程的控制对象中
  2. 注册所有配置的插件,让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
  3. 从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。
  4. 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
  5. 递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。
  6. 输出所有chunk到文件系统。

入口和上下文(entry and context)

entry

指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始

  • 单入口

entry: './src/app.js'

等同于下面写法:

 entry: {
    main: './src/app.js'
  }
复制代码
  • 对象语法

分离 应用程序(app) 和 第三方库(vendor) 入口

  entry: {
    app: './src/main.js',
    vendor: './src/jquery.js'
  }
复制代码

这告诉我们 webpack 从 main.js 和 jquery.js 开始创建依赖图(dependency graph)。这些依赖图是彼此完全分离、互相独立的。

context

webpack 编译时的基础目录,entry和loader 会相对于此目录查找

默认值为当前目录,不建议修改

出口(output)

告诉 webpack 在哪里输出它所创建的 bundles

output: {
    path: path.join(__dirname, 'dist'),
    publicPath: '/',
    filename: 'js/[name].js'
}
复制代码

publicPath

用于指定打包后的文件需要加载的外部资源(如图片、js、css等)的跟路径

默认值是一个空字符串 "",通常设置成"/"
静态资源最终访问路径 = output.publicPath + 资源loader或插件等配置路径

  • loader 输出图片文件配置
{ name: 'imgs/[name].[ext]' }
// 那么图片最终的访问路径为
output.publicPath('/') + 'imgs/[name].[ext]' = '/imgs/[name].[ext]'
复制代码
  • plugin 提取css文件配置:
new ExtractTextPlugin('css/[name].[contenthash:10].css')
// html中加载css打包后代码
<link href="/css/app.9502b0c565.css" rel="stylesheet">
复制代码
  • html中加载js打包后代码
<script type="text/javascript" src="/js/runtime.4ece365fd5.js"></script>
复制代码

path

打包文件输出的目录

建议绝对路径;默认值为当前路径。
path 中用使用 [hash] 模板可用于版本回归

output: {
    path: path.resolve('./dist/[hash:8]/')
}
复制代码

loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)

注意:module.loaders 改为 module.rules;链式loader

  • webpack1语法
module: {
    loaders: [{
        test: /\.less$/,
        loader: "style!css!less"
    })
}
复制代码
  • webpack2、3语法
module: {
    rules: [{
        test: /\.less$/,
        use: [
            "style-loader",
            "css-loader",
            "less-loader"
        ]
    }]
}
复制代码

文件类

raw-loader 加载文件原始内容,比如.txt文件

file-loader 将文件发送到打包后文件夹中

{
    test: /\.(gif|png|jpe?g|svg)$/,
    use: [{
        loader: 'file-loader',
        options:{}
    }]
}
复制代码

默认输出到output的根目录下,name为32为hash值

名称 类型 默认值 描述
name {String | Funciton} [hash].[ext] 自定义文件名

placeholders

名称 类型 默认值 描述
[ext] {String} file.extname 资源扩展名
[name] {String} file.name 资源名
[path] {String} file.dirname 资源路径
[hash] {String} md5 内容的哈希值

url-loader 功能类似file-loader

对file-loader的扩展,可以设置小图片转换base64图片

{
    test: /\.(png|jpg|gif)$/,
        use: [
        {
            loader: 'url-loader',
            options: {
                limit:5000,
                name: 'img/[name].[ext]?[hash]'
            }
        }
    ]
}
复制代码

转换编译类

script-loader

在全局上下文(global context)执行一次 JS脚本,就像你在网页上通过<script>把它们引进来一样。

babel-loader

由于浏览器只能读懂ES5语法,需要babel将ES2015+语法编译为ES5语法

  1. 安装
    npm install babel-loader babel-core babel-preset-env webpack
  2. 用法
{
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {}
    }
}
复制代码

options

  • cacheDirectory:默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果
  • babelrc 默认true,设置false,.babelrc将不会启用

ts-loader 像 JavaScript 一样加载 TypeScript 2.0+

模版类

html-loader 解决html里加载图片问题

minimize:true 压缩html文件

handlebars-loader 加载handlebars文件并编译为html文件

handlebars-template-loader 解决handlebars图片路径问题

markup-inline-loader

将内联的 SVG/MathML 文件转换为 HTML。在应用于图标字体,或将 CSS 动画应用于 SVG 时非常有用

{
    test: /\.html$/,
    use: [
      'html-loader',
      'markup-inline-loader'
    ]
}
复制代码

html中使用:
<img markup-inline src="./_images/camera.svg" />
<img data-markup-inline src="./_images/camera.svg" />

样式类

style-loader 将模块的导出作为样式添加到 DOM 中

css代码放入js代码里再加入到html里

css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码

得到css代码

less-loader 加载和转译 LESS 文件

sass-loader 加载和转译 SASS/SCSS 文件

得到css文件

postcss 使用 PostCSS 加载和转译 CSS 文件

后处理css文件,对css文件做语法分析,真正的核心操作,依赖于postcss庞大的插件群体 postcss插件
比如css的语法验证,压缩,支持变量和混入语法

  • autoprefixer 补全浏览器前缀
  • postcss-import css文件中使用@import引入其他样式文件,但是使用autoprefixer发现,import进来的样式没有处理
postcss: function(webpack) {
  return [
    postcssImport({
      addDependencyTo: webpack
    }),
    autoprefixer
  ]
}
复制代码

清理和测试类

mocha-loader 使用 mocha 测试(浏览器/NodeJS)

eslint-loader 使用 ESLint 清理代码

jshint-loader 使用 JSHint 清理代码

框架类

vue-loader 加载和转译 Vue 组件

angular2-template-loader 加载和转译 Angular 组件

插件

loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量

html-webpack-plugin 生成html文件

  • 为html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题
  • 可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口
new htmlWebpackPlugin(options)
复制代码
名称 类型 默认值 描述
title {String} `` 用于生成的HTML文档的标题
filename {String} 'index.html' 生成html的文件名
template {String} `` 模版路径及文件名(路径相对与output.context)
inject {Boolean|String} true 当传递true或'body'所有JavaScript资源将被放置在正文元素的底部。'head'将脚本放置在head元素中,false不会将脚本放进html中
favicon {String} `` 将给定的图标路径添加到输出HTML
minify {Boolean|Object} true 缩小输出html html-minifier
hash {Boolean} false true:将webpack所有包含的脚本和CSS文件附加一个独特的编译哈希。这对缓存清除非常有用
cache {Boolean} true 仅在文件被更改时才发出文件
showErrors {Boolean} true 错误细节将写入HTML页面
chunks {?} ? 允许添加的chunk名
chunksSortMode {String|Function} auto 允许控制chunk在被包含到HTML之前应该如何排序。允许的值是'none'不分类 | 'auto'按块ID排序 | 'dependency'通过其“父项”属性对块之间的依赖关系进行排序 | 'manual' | {Function}
excludeChunks {String} `` 允许您跳过一些chunk
xhtml {Boolean} false 如果true将link标签呈现为自动关闭(符合XHTML)
  • chunks按照顺序加载
chunksSortMode: function (chunk1, chunk2) {
    var order = ['common', 'public', 'index'];
    var order1 = order.indexOf(chunk1.names[0]);
    var order2 = order.indexOf(chunk2.names[0]);
    return order1 - order2;  
}
复制代码

extract-text-webpack-plugin

webpack 把所有的资源都当成了一个模块, CSS,Image, JS 字体文件资源, 都打包到一个 bundle.js 文件中

它将*.css输入块中的所有必需模块移动到单独的CSS文件中
new ExtractTextPlugin([id: string], filename: string, [options])

  1. id 该插件实例的唯一标志,一般是不会传的,其自己会生成。
  2. filename 文件名。可以是[name]、[id]、[contenthash] [name]:将会和entry中的chunk的名字一致 [id]:chunk 的数量 [contenthash]:根据内容生成hash值
  3. options allchunk:是否将所有额外的chunk都压缩成一个文件 disable:禁止使用插件 ignoreOrder: 禁用顺序检查 (这对 CSS 模块很有用!),默认 false
  • extract 配置:
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",//编译后用什么loader来提取css文件
          publicPath:, //重写此 loader 的 publicPath 配置
          use: "css-loader" //需要什么样的loader去编译文件
        })
      }
    ]
  }
复制代码

CopyWebpackPlugin 拷贝资源插件

CopyWebpackPlugin([
        {
            context: 'global/img',
            from: '**/*',
            to:'img/common'
        },
        {
            from: 'img',
            to:'img'
        },
        {
            from :'global/lib/es5-shim-sham.js'
        }
])
复制代码

from 定义要拷贝的源目录.
to 定义要拷盘的目标目录.
context 上下文.
flatten 只拷贝文件不管文件夹 默认是false.
ignore 忽略拷贝指定的文件 可以用模糊匹配.
force 强制覆盖先前的插件 可选 默认false.

UglifyJsPlugin 压缩js文件

new webpack.optimize.UglifyJsPlugin(options)
复制代码
名称 类型 默认值 描述
test {RegExp|Array<RegExp>} /.js$/i 匹配文件
include {RegExp|Array<RegExp>} undefined 包含目录
exclude {RegExp|Array<RegExp>} undefined 非匹配目录
cache {Boolean|String} false 启用文件缓存;cache: 'path/to/cache'
parallel {Boolean|Number} false 使用多进程并行运行来提高构建速度
sourceMap {Boolean} false 使用源映射将错误消息位置映射到模块(这会减慢编译速度) ⚠️ cheap-source-map 选项不适用于这个插件
uglifyOptions {Object} {...defaults} uglify 选项

webpack.optimize.UglifyJsPlugin()遇到的 Unexpected token: operator (>) from UglifyJs问题可以使用 uglifyjs-webpack-plugin插件

DefinePlugin 允许在编译时(compile time)配置的全局常量

new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify('production')
})
复制代码

CleanWebpackPlugin 打包前清理dist目录

new CleanWebpackPlugin(['./dist'])

webpack自带插件

CommonsChunkPlugin 提取 chunks 之间共享的通用模块

webpack将多个模块打包之后的代码集合称为chunk

将公共js代码提取到单独文件里

  • 是一个可选的用于建立一个独立文件(又称作 chunk)的功能
  • 通过将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用
  • 语法: new webpack.optimize.CommonsChunkPlugin(options)
  • 配置
  1. 决定生成chunk的参数: name, names, async
  • name: string: 公共chunk的名字
  • names: string[]: 和name一样,不过传入的是一个数组
  • async: boolean|string: 把公共代码提取到一个懒加载的chunk,在被使用到时才进行下载
  1. 决定被提取的chunk: chunks, children, deepChildren
  2. 决定提取条件: minChunks
  • minChunks: number|infinity|function(module,count)->boolean: 如果传入数字(默认值为3),就是告诉webpack,只有当模块重复的次数大于等于该数字时,这个模块才会被提取出来;Infinity表示不会打包多余的第三方库;当传入为函数时,所有符合条件的chunk中的模块都会被传入该函数做计算,返回true的模块会被提取到目标chunk
module.exports = {
    entry: {
        main1: '/src/main1.js',
        main2: '/src/main2.js',
        jquery:["jquery"],
        vue:["vue"]
    },
    plugins: [
        new CommonsChunkPlugin({
            name: ["common",'jquery','vue'],//对应于上面的entry的key
            minChunks:2
        })
    ]
};
复制代码

打包后jquery和vue会生成独立chunk,main1和main2中的公共业务模块会打包到common.js中;minChunks为infinity时,公共业务模块会分别打包到main1.js和main2.js中
webpack用插件CommonsChunkPlugin进行打包的时候 将符合引用次数(minChunks)的模块打包到name参数的数组的第一个块里(chunk),然后数组后面的块依次打包,最后一个块包含webpack生成的在浏览器上使用各个块的加载代码

HotModuleReplacementPlugin 热模块替换

new webpack.HotModuleReplacementPlugin()
复制代码
devServer: {
    hot: true // 激活服务器的HMR
}
复制代码

其他

cross-env

在不同系统环境下设置变量

webpack-dev-server

  • 开发环境用于调试报错信息等,生成一个开发用的服务器,在文件有变化的时候自动给我们打包,然后刷新页面
  • 它还有个模块热替换的功能 .. 就是它可以只替换有变化的地方 .. 不需要刷新整个页面 ...

优化

优化输出

压缩css

css-loader?minimize去除css文件里有很多空格和tab

tree-shaking

借助es6 import export语法静态性的特点来删掉export但是没有import过的东西

  • 配置babel让它在编译转化es6代码时不把import export转换为cmd的module.export,配置如下:
"presets": [
    [
      "es2015",
      {
        "modules": false
      }
    ]
]
复制代码

优化 UglifyJsPlugin

new UglifyJsPlugin({
    // 最紧凑的输出
    beautify: false,
    // 删除所有的注释
    comments: false,
    compress: {
      // 在UglifyJs删除没有用到的代码时不输出警告  
      warnings: false,
      // 删除所有的 `console` 语句
      // 还可以兼容ie浏览器
      drop_console: true,
      // 内嵌定义了但是只用到一次的变量
      collapse_vars: true,
      // 提取出出现多次但是没有定义成变量去引用的静态值
      reduce_vars: true,
    }
})
复制代码

imagemin-webpack-plugin压缩图片

更快的构建

缩小文件搜索范围

  • 配置模块库.
    在js里出现import 'redux'这样不是相对也不是绝对路径的写法时会去node_modules目录下找。但是默认的配置会采用向上递归搜索的方式去寻找node_modules,但通常项目目录里只有一个node_modules在项目根目录
module.exports = {
    resolve: {
        modules: [path.resolve(__dirname, 'node_modules')]
    }
};
复制代码
  • 配置loader
{
    test: /\.js$/,
    loader: 'babel-loader',
    include: path.resolve(__dirname, 'src')
}
复制代码

开启 babel-loader 缓存

babel编译过程很耗时,好在babel-loader提供缓存编译结果选项,在重启webpack时不需要创新编译而是复用缓存结果减少编译流程。babel-loader缓存机制默认是关闭的,打开的配置如下:

{
test: /\.js$/,
      loader: 'babel-loader?cacheDirectory',
}
复制代码

使用 alias

resolve.alias 配置路径映射。 发布到npm的库大多数都包含两个目录,一个是放着cmd模块化的lib目录,一个是把所有文件合成一个文件的dist目录,多数的入口文件是指向lib里面下的。
默认情况下webpack会去读lib目录下的入口文件再去递归加载其它依赖的文件这个过程很耗时,alias配置可以让webpack直接使用dist目录的整体文件减少文件递归解析。配置如下:

module.exports = {
  resolve: {
    alias: {
      'moment': 'moment/min/moment.min.js',
      'react': 'react/dist/react.js',
      'react-dom': 'react-dom/dist/react-dom.js'
    }
  }
};
复制代码

使用 noParse

module.noParse 配置哪些文件可以脱离webpack的解析。 有些库是自成一体不依赖其他库的没有使用模块化的,比如jquey、momentjs、chart.js,要使用它们必须整体全部引入。
webpack是模块化打包工具完全没有必要去解析这些文件的依赖,因为它们都不依赖其它文件体积也很庞大,要忽略它们配置如下:

module.exports = {
  module: {
    noParse: /node_modules\/(jquey|moment|chart\.js)/
  }
};
复制代码
文章分类
前端