Webpack学习小结

v3

https://doc.webpack-china.org/guides/

2018.2.8 星期四 16:42

一 SMTC

webpack.test.conf.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
const path=require('path')
const webpack=require('webpack')
const HtmlWebpackPlugin=require('html-webpack-plugin')
const CleanWebpackPlugin=require('clean-webpack-plugin')

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractLess = new ExtractTextPlugin({
filename: "[name].[contenthash].css",
// disable: process.env.NODE_ENV === "development"
});

module.exports={
context:path.resolve(__dirname, '../'),
entry:{
// main:['./src/js/app1.js','./src/js/app2.js'],
main:['./src/js/app.js','./src/js/common.js'],
vendor:['./src/js/lib/tab.js','./src/js/lib/lottery.js'],
polyfills:['./src/js/polyfills.js'],
// dll:['mediasoup-client']
// dll:['mediasoup-client','ocanvas']
},
output:{
// filename:'[name].[hash].js',
filename:'[name].bundle.js',
path:path.resolve(__dirname,'./dist'),
// path:__dirname + './dist',
publicPath:'/'
// library: '[name]', // 当前Dll的所有内容都会存放在这个参数指定变量名的一个全局变量下,注意与DllPlugin的name参数保持一致
},
// devtool:'cheap-module-eval-source-map',
devtool:'inline-source-map',
// devtool:'source-map',
devServer:{
contentBase:['./dist','./'],
port:9000,
compress: true,
hot:true
},
resolve:{
extensions:['.js','.less'],
alias:{
'data':path.resolve(__dirname,'../data/'),
'ajs':path.resolve(__dirname,'../assets/js'),
'acss':path.resolve(__dirname,'../assets/css'),
'aimg':path.resolve(__dirname,'../assets/images'),
'lib':path.resolve(__dirname,'../src/js/lib'),
'components':path.resolve(__dirname,'../src/css/components')
}
},
module:{
rules:[{
// enforce:'pre',
// test:/\.js$/,
// loader:['eslint-loader'],
// include:path.resolve(__dirname,'../src/js'),
// // exclude:/node_modules/,
// options:{
// // configFile: path.resolve(__dirname,'../.eslintrc.js'), // 指定eslint的配置文件在哪里
// eslintPath: path.resolve(__dirname,'../.eslintrc.js'), // 指定eslint的配置文件在哪里
// failOnWarning: false, // eslint报warning了就终止webpack编译
// failOnError: true, // eslint报error了就终止webpack编译
// cache: true, // 开启eslint的cache,cache存在node_modules/.cache目录里
// }
},{
test:/\.css$/,
use:ExtractTextPlugin.extract({
fallback:"style-loader",
use:"css-loader"
}),
// loader:ExtractTextPlugin.extract(
// "style-loader",
// "css-loader?sourceMap"
// ),
include:path.resolve(__dirname, '../src/css')
},{
test:/\.less/,
use:extractLess.extract({
use: [{
loader: "css-loader"
}, {
loader: "less-loader",
options:{
strictMath:true,
noIeCompat:true,
sourceMap:true
}
}],
// use style-loader in development
fallback: "style-loader"//
}),
include:path.resolve(__dirname,'../src/css/')
},{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader:'url-loader?limit=8192&name=./static/img/[hash].[ext]', // <= 8kb的图片base64内联
// use:['url','file-loader'],
include:path.resolve(__dirname,'../src/images/'),
query: {
// limit: 10000,
// name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url',
include:path.resolve(__dirname,'../src/fonts'),
query: {
limit: 10000,
// name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
name: './static/fonts/[name].[hash].[ext]'
}
},{
// 如果你所有的jQuery插件都是用webpack来加载的话,
// 的确用ProvidePlugin就足够了;但总有那么些需求是只能用<script>来加载的。
test: require.resolve('jquery'), // 此loader配置项的目标是NPM中的jquery
loader: 'expose-loader?$!expose-loader?jQuery', // 先把jQuery对象声明成为全局变量`jQuery`,再通过管道进一步又声明成为全局变量`$`
},{
test:require.resolve('../assets/js/iscroll.js'),
loader: 'exports-loader?IScroll'
}],
// 防止 webpack 解析那些任何与给定正则表达式相匹配的文件。忽略的文件中不应该含有 import, require, define 的调用,或任何其他导入机制。忽略大型的 library 可以提高构建性能。
noParse:/iscroll/
// 从webpack 3.0.0开始
// noParse:content=>{return /iscroll/.test(content)}
},
plugins:[
new ExtractTextPlugin('[name].css'), // 打包css/less的时候会用到ExtractTextPlugin
extractLess,

new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV':JSON.stringify('devlopment')
}),
new webpack.HashedModuleIdsPlugin(),

new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: "vendor.js",
chunks: ['vendor','dll'],
minChunks:3,
// async:true,
// children:true
}),
/* new webpack.DllPlugin({
path: 'manifest.json', // 本Dll文件中各模块的索引,供DllReferencePlugin读取使用
name: '[name]', // 当前Dll的所有内容都会存放在这个参数指定变量名的一个全局变量下,注意与参数output.library保持一致
context:path.resolve(__dirname, '../'), // 指定一个路径作为上下文环境,需要与DllReferencePlugin的context参数保持一致,建议统一设置为项目根目录
}), */
/* 跟业务代码一样,该兼容的还是得兼容 */
new webpack.ProvidePlugin({
$:'jquery',
jQuery:'jquery',
'window.jQuery':'jquery',
'window.$':'jquery'
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
title:'htmlwebpackplugin title',
filename: 'index.html',
template: './src/index.html',
favicon:'./src/favicon.ico',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
excludeChunks:['polyfills']
// chunks:['main','vendor'],
// minify:false
})
],
// 配置了这个属性之后 vue 和 vue-router这些第三方的包都不会被构建进 js 中,那么我们就需要通过 cdn 进行文件的引用了
//externals对象的key是给require时用的,比如require('vue'),,对象的value表示的是如何在global(即window)中访问到该对象,这里是window.Vue
externals:{
// jquery:'window.jQuery',
// mediasoupclient:'mediasoup-client'
},
// target:'web'//default
}

2018.2.11 星期日 14:32

二 配置说明

2.1 context,entry , module.rules[0].test 和module.rules[0].loader

  1. context:基础目录,绝对路径,用于从配置中解析入口起点(entry point)和 loader
  2. entry:字符串:指向具体入口文件
  3. test:请求文件的绝对路径。它已经根据 resolve 规则解析。
    正则,require.resolve('../assets/js/iscroll.js')
  4. path:,指向路径
  5. loader:字符串,指向某个loader

    path.resolve 和path.join用法和区别

2.2 output.publicPath 和 devServer.contentBase

  1. output.publicPath:webpack-dev-server 也会默认从 publicPath 为基准,使用它来决定在哪个目录下启用服务,来访问 webpack 输出的文件。
  2. devServer.contentBase:指定服务器提供静态资源的路径,devServer启动后,可以根据该contentBase(可以数组,指定过个资源路径)引入资源

2.3 module的几个常见概念

  1. modules.rules[0].resourec :module.rules[0].test配合 module.rules[0].include/excluede的使用
  2. module.rules[0].test == module.rules[0].resource.test

    如果你提供了一个 Rule.test 选项,就不能再提供 Rule.resource

  3. 和module.rules[0].loaders==module.rules[0].use
  4. module.rules[0].loader==module.rules[0].use:[{loader}]
  5. module.rules[0].rules 好像见过

2.4 第三方库的引入:shimming,CommonsChunkPlugin,DllPlugin和UglifyJSPlugin

/shimming/

  1. ProvidePlugin:把模块作为应用程序中的一个全局变量。
  2. imports-loader: 模块需要某个依赖时使用,比如this指向window,bootstrap需要jQuery
  3. exports-loader:将一个全局变量作为一个普通的模块来导出,需要import
  4. expose-loader: 暴露一个全局变量,不需要import。注意和ProvidePlugin的区别:??ProvidePlugin可以暴露jQuery1.11.3的全局变量吗

    The expose loader adds modules to the global object. This is useful for debugging, or supporting libraries that depend on libraries in globals.

  5. script-loader:估计和expose差不多

/构建性能/

  1. noParse:expose,script,还有exports的模块应该可以用module.noParse配置

    noParse:防止 webpack 解析那些任何与给定正则表达式相匹配的文件。忽略的文件中不应该含有 import, require, define 的调用,或任何其他导入机制。忽略大型的 library 可以提高构建性能。

  2. module.externals :有人说不符合webpack模块管理了??

    防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
    例如,从 CDN 引入 jQuery,而不是把它打包:

  3. DllPlugin:??如果不用externals排除,用该方法剥离不经常改动的第三方依赖

    使用 DllPlugin 将更改不频繁的代码进行单独编译。这将改善引用程序的编译速度,即使它增加了构建过程的复杂性。
    DLLPlugin 和 DLLReferencePlugin 用某种方法实现了拆分 bundles,同时还大大提升了构建的速度。

/代码分离/

  1. CommonsChunkPlugin:提取公共子模块,提供缓存。属于代码分离–防止重复。公共模块提出来,利用缓存,没有修改的部分,hashID不会更改

    CommonsChunkPlugin,是一个可选的用于建立一个独立文件(又称作 chunk)的功能,这个文件包括多个入口 chunk 的公共模块。通过将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存起来到缓存中供后续使用。这个带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件。
    将第三方库(library)(例如 lodash 或 react)提取到单独的 vendor chunk 文件中,是比较推荐的做法,这是因为,它们很少像本地的源代码那样频繁修改。因此通过实现以上步骤,利用客户端的长效缓存机制,可以通过命中缓存来消除请求,并减少向服务器获取资源,同时还能保证客户端代码和服务器端代码版本一致。这可以通过使用新的 entry(入口) 起点,以及再额外配置一个 CommonsChunkPlugin 实例的组合方式来实现:

  2. ExtractTextPlugin:也属于代码分离,用于将 CSS 从主应用程序中分离

/精简输出/

  1. UglifyJSPlugin:Tree Shaking–精简输出

2.5 DLLPlugin ,CommonsChunkPlugin,noparse 还是external

教你如何玩转webpack.DllPlugin #6
https://github.com/superpig/blog/issues/6

1 问题场景:

首先单独打包第三方库文件,其次想抽取不同入口的common部分。因为该项目是多入口结构,AngularJS入口的库文件都是通过公司CDN引入,只有vue.html入口下的库文件通过npm引入,我们希望打包的Vue相关的库文件只用于vue.html入口下。

2 解决方案:

  1. CDN & 配置externals
    第三方库文件通过公司CDN引入,webpack配置extenals将依赖的库指向全局变量,从而不再打包库文件。这种方案是可以满足我们的需求的,但是配置externals存在不完美的地方,这篇文章有详细介绍
  2. CommonsChunkPlugin
    webpack的CommonsChunkPlugin插件,官网提供了抽取vendor的解决思路,其中manifest的作用防止再次构建时改变vendor的hash,导致无法利用浏览器的缓存。同时,我们还需要抽取不同入口的common部分,所以综上,理想的webpack配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.export = {
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
minChunks: function(module){
return module.context && module.context.indexOf("node_modules") !== -1;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest",
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
filename: 'js/common.[chunkhash].js',
minChunks: 2
})
]
}

然而,实际效果是webpack只打包了vendor库文件,不同入口下的common部分没提取出来。所以,该方案无法满足需求。
$_PS: common建立第三个buddle,不可行吗??

  1. webpack.DllPlugin & add-asset-html-webpack-plugin
    webpack.DllPlugin的作用是打包一个“动态链接库(dll)”,dll包就是我们的库文件,用于业务代码引用,而且打包业务代码时,不需要再次打包dll包。使用这个功能,我们需要两个步骤:
  • 打包dll包
  • 引用dll包,打包业务代码
    为了打包dll包,我们需要单独配置一个dll.config.js:
    接着,就是我们如何在打包业务代码时引用vendor.dll.js:
  1. webpack.DllPlugin & html-webpack-include-assets-plugin
    打包dll包的过程同上,所以直接看html-webpack-include-assets-plugin的使用配置。
    这里多用一个CopyWebpackPlugin插件,将vendor.dll.js文件拷贝到build目录。注意使用HtmlWebpackIncludeAssetsPlugin时,需要将append参数设置为false,保证vendor.dll.js在业务代码之前插入。

这里多用一个CopyWebpackPlugin插件,将vendor.dll.js文件拷贝到build目录。注意使用HtmlWebpackIncludeAssetsPlugin时,需要将append参数设置为false,保证vendor.dll.js在业务代码之前插入。

2.6 DIIPlugin

1 使用

彻底解决Webpack打包性能问题

https://zhuanlan.zhihu.com/p/21748318

  1. 首先我们来打包ddl包,首先配置一个这样的 ddl.config.js:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const webpack = require('webpack');
    const vendors = [
    'react',
    'react-dom',
    'react-router',
    // ...其它库
    ];
    module.exports = {
    output: {
    path: 'build',
    filename: '[name].js',
    library: '[name]',
    },
    entry: {
    "lib": vendors,
    },
    plugins: [
    new webpack.DllPlugin({
    path: 'manifest.json',
    name: '[name]',
    context: __dirname,
    }),
    ],
    };

运行Webpack,会输出两个文件一个是打包好的 lib.js,一个就是 manifest.json,它里面的内容大概是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "vendor_ac51ba426d4f259b8b18",
"content": {
"./node_modules/react/react.js": 1,
"./node_modules/react/lib/React.js": 2,
"./node_modules/react/node_modules/object-assign/index.js": 3,
"./node_modules/react/lib/ReactChildren.js": 4,
"./node_modules/react/lib/PooledClass.js": 5,
"./node_modules/react/lib/reactProdInvariant.js": 6,
// ............
}
}

  1. 接下来我们就可以快乐地打包业务代码啦,首先写好打包配置文件 webpack.config.js:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const webpack = require('webpack');
    module.exports = {
    output: {
    path: 'build',
    filename: '[name].js',
    },
    entry: {
    app: './src/index.js',
    },
    plugins: [
    new webpack.DllReferencePlugin({
    context: __dirname,
    manifest: require('./manifest.json'),
    }),
    ],
    };

2 其他

预打包Dll,实现webpack音速编译
http://array_huang.coding.me/webpack-book/chapter4/webpack-dll.html
在每个页面里,都要按这个顺序来加载js文件:Dll文件 => CommonsChunkPlugin生成的公用chunk文件(如果没用CommonsChunkPlugin那就忽略啦) => 页面本身的入口文件。

有两个注意事项:

  • 如果你是像我一样利用HtmlWebpackPlugin来生成HTML并自动加载chunk的话,请务必在<head>里手写<script>来加载Dll文件。
  • 为了完全分离源文件和编译后生成的文件,也为了方便在编译前可以清空build目录,不应直接把Dll文件编译生成到build目录里,我建议可以先生成到源文件src目录里,再用file-loader给原封不动搬运过去。

3 思考

  1. DII应该是打包了,只不过是单独打包
  2. 大的第三方库,不是CDN就是该方法了,比如:react、angular、jQuery3+等
  3. 老旧库(没有export,不能import)是不是需要提供shimming也可以DII,否则只能external了
  4. 引入的静态资源:图片、css、字体等是否也是这样打包
  5. 需要注意的可能是生成dll文件的,路径引入问题

2.7 bootstrap配置

$_TODO:

三 实际

webpack简单和复杂的地方:配置。由于高度可配置,可以灵活使用,没法确定哪一个一定是最优的选择。
但是,需要选择一个最适合实际项目的配置

3.1 需求分析

目前,基本没什么需求。
没有确定需要使用的第三方依赖,除mediasoup。
只要求,conference-client独立出来

3.2 实现

  1. 由于使用webpack,不使用external,DII单独打包第三方依赖(不改动),commonsChunk打包提取项目开发中的公共库。
    1. 没有了CDN或者页面引入,包括jQuery,iScroll等。所有依赖都是库都通过DII打包
    2. noparse也不需要了
    3. 开发和生产中,只需要打包一次就行了;除非三方依赖发生改变。
  2. 上述方案,并没有考虑引入bootstrap等css,font静态资源。
  3. 开发中只生成一个js文件,一个css文件。所有依赖都打包,包括老旧库,使用import方式引用
  4. 要求生成一份conference-client.min.js,需要参照library,额外配置,发布一份js
  5. 非现代浏览器用babel-polyfill,因为我们只需要chrome,useBuildin:true
  6. conference-client可能需要提供ES5。因为是库,需要babel-loader,preset:env,babel-runtime
  7. 小组/个人开发,不提供eslint(也没有配置成功,可配置编辑器)

2.11 星期日 17:22

knowledge is no pay,reward is kindness
0%