webpack详细项目配置实用教程

webpack是一款打包工具,它是模块化开发的模式、优化了加载速度;
webpack有什么特色?

这些已有的模块化工具并不能很好的完成如下的目标:

  • 将依赖树拆分成按需加载的块
  • 初始化加载的耗时尽量少
  • 各种静态资源都可以视作模块
  • 将第三方库整合成模块的能力
  • 可以自定义打包逻辑的能力
  • 适合大项目,无论是单页还是多页的 Web 应用

一、初始化配置

npm init //npm 初始化 生成 package.json 配置文件

首先是package.json文件,它不是webpack的组成部分,但是常和webpack项目出双入对,先看一下它的大概模样:

webpack详细项目配置实用教程
1. scripts 命令行脚本通过key:value的方式描述。key是脚本名,value是脚本执行的内容,通过在命令行中输入npm run 脚本名 就可以执行。这一块的内容是实际开发中很实用的,这里不详情展开,参考地址关于这个文件的更多介绍,请移步官方内容 这里我只重点介绍一下以下内个内容:

常见的脚本名有:npm run start , npm run test 。 内置的脚本名(比如start),可以省略run。

2. devDependencies 开发依赖,相应的还有一个dependencies(可以理解为生产环境依赖)

通过npm install 包名 --save-dev (保存到devDependencies),或 --save 保存到(dependencies)

package.json是用来配合包的管理和发布用的,如果你不想发布这个项目,似乎以上内容对项目开发并没有什么好处,但是作为团队协作,它可以方便自己和同事快速搭建项目,管理项目中用到的第三方包。

二、创建webpack配置文件 webpack.config.js

npm install webpack --save-dev //给项目安装webpack 同时创建webpack.config.js文件

module.exports={

  1. 单 入口 与 输出 实例

    entry:'./js/index.js',

    output:{

    path:__dirname,(如果指定路径必须是绝对路径)
//__dirname 代表所有打包的bundle.js的文件位置和入口文件index.js 同一个目录下

path:path.join(_dirname,'dist') (把打包文件打入dist目录下边)

filename:'bundle.js'

}

}

2、多页面配置入口实例 与 输出 实例

var path=require('path');

module.exports={

entry: {

  bundle1: './main1.js', //入口1

  bundle2: './main2.js'//入口2

},

output: {

path:__dirname, (如果指定路径必须是绝对路径)

path:path.join(_dirname,'dist') ,

publicPath:'/dist/' //在对应的html中引入 dist 目录下对应的打包文件

filename: '[name].bundle.js' // [name]是一个变量,会自动替换成entry的key

 }

}

这种用法,对于入口文件需要指定多种类型的文件时比较有用。比如['./main1.js','./main1.css'],
后面用到再细讲。

小结一下:对于entry一共展示了三种形式:

  1. entry:'app.js'  直接写入口文件

  2. entry:{bundle:'./main1.js'} 对象形式

  3. entry:{bundle:['./main1.js']} 对象中的值用数组表示

3、多文件合并一个文件

有两个文件,app.js,cats.js,需要把它们合并成一个bundle.js文件

cats.js:

1

2

var cats = ['dave', 'henry', 'martha'];

module.exports = cats;

app.js:

1

2

cats = require('./cats.js');

console.log(cats);

webpack.config.js :

3

4

5

6

7

8

9

//最简单的webpack配置

module.exports = {

    entry: './app.js', //入口文件地址

    output: {

        filename: 'bundle.js',  //打包后的文件名

    }

};

三、配置常用的loader(加载器)

1)、css loader

npm install css-loader style-loader --save-dev (安装当前项目依赖下)

var path=require('path');

var webpack=require('webpack');

module.exports={

entry: {

  bundle1: './main1.js', //入口1

  bundle2: './main2.js'//入口2

},

output: {

path:__dirname, //(如果指定路径必须是绝对路径)

path:path.join(_dirname,'dist') , 二个path选一个

publicPath:'/dist/' //在对应的html中引入 dist 目录下对应的打包文件

注意:这个publicPath原本是用来配置虚拟目录用的,也就是通过http方式访问时的路径,或者通过webpack HMR方式加载时的输出目录。在这里只能算是一种hack用法。说到output,就要提一下文件缓存[hash]的用法:

1

2

3

4

5

output: {

  path:'./dist',

  publicPath:'./dist/',

  filename: 'bundle_[hash:8].js' //通过:8截取has值的前8位

},

  这个[hash]作用很少被提到,在实际开发中,是很常见的功能,原样输出的hash我觉得太长,可以通过[hash:加数字]的方式进行截取,很方便。

filename: '[name].bundle.js'  // [name]是一个变量,会自动替换成entry的key

 },

plugins:[

new webpack.optimize.uglifyJsPlugin() //对所有打包的文件进行压缩

],

module:{

    loaders:[

        {test:/\.js$/,loader:'babel'},

        {test:/\.css$/,loader:'style!css'} //匹配已 .css 为扩展名的文件

        //test是对该类文件的正则表达式,用来判断采用这个loader的条件,例如 .css 为扩展名的文件

]

}

3.10.0版本

module:{

    rules:[{

        {test:/\.css$/} //匹配已 .css 为扩展名的文件

        use:[

        "style-loader",

        "css-loader"

]

}]

}

}

这里有两个要注意的地方:

  1。 对于有多个加载器串联的情况,webpack,它是从右向左依赖加载的,也就是说先用css-loader,再用style-loader.

  2.  为什么会有一个style-loader, 因为webpack默认是把css文件插在html文件内,通过style标签加载样式的。所以需要用style-loader这个加载器。

如果想要把css用文件的形式link到html中,也是可以的,后面会讲到。

由于我们用了css加载器,所以入口文件其实也可以直接写成:entry:'./app.css'; 效果是一样的。这就体现了,入口文件,不一定要是js格式,只要有对应的加载器,就可以直接在入口中使用,甚至多种类型混合使用,比如['./app.js','app.css'],都是可以的。


webpack 中  css-loader 高阶应用     css作用域 与 css Modules
目的:解决团队开发时类名定义冲突问题

在webpack.config.js 下 css-loader里边配置 modules:true

webpack详细项目配置实用教程
它的原理是将同样的css 类名 webpack 里边的 hash 把类名替换成唯一的类名 从而避免冲突;


css-loader  高阶应用  自动分离CSS到独立文件

  1. 首先安装
    npm installl  extract-text-webpack-plugin --save-dev  

webpack详细项目配置实用教程

    2.然后在webpack.config.js 引入extract-text-webpack-plugin 并配置
    webpack详细项目配置实用教程webpack详细项目配置实用教程
其中filename  的值  name 是参数 css文件定义的 app.js  那么name就是appl
ignoreOrder  参数 代表 是否跳过排序  默认设置为 true

2)、img loader

module:{

    loaders:[

    {test:/\.js$/,loader:'babel'},

    {test:/\.css$/,loader:'style!css'}, //匹配已 .css 为扩展名的文件

    {test:/\.png$/,loader:'file'} , //匹配已 .png为扩展名的文件

    {test:/换成文件夹路径images/,loader:'url'}

/*项目中icon比较多的时候用webpack的‘’url‘’ loader 会把图片路径转化base64 从而减少http请求*/

 

]

}

3)、Sass loader

当我们用sass编写css样式的时候就可以用到

{test:/\.scss$/,loader:'sass'} ,

四、安装sourcemap调试工具

webpack --devtool source-map 为打包的文件生成 .map 文件

 

在js文件中写入debugger断点

 

运行webpack 刷新浏览器 出现debugger断点

 

在webpack配置文件中安装调试工具

例如如下代码:

module.exports={

    entry:'./js/index.js',

    output:{

    path:__dirname,

    filename:'bundle.js'

},

devtool:'source-map', //便于debugger调试,否则在浏览器的source中看不到自己的代码都是webpack的代码不利于调试

module:{

    loaders:[

    {test:/\.css$/,loader:'style!css'} //匹配已 .css 为扩展名的文件

]

}

}

五、安装babel转换器 (在用JSX、ES6等使用)

npm install babel-loader babel-core babel-preset-es2015 --save-dev

然后创建 .babelrc文件 并写入es-2015的预设

例如如下代码:

{

    "presets":["es-2015"]

}

然后再webpack.config.js中配置一下什么样的文件需要使用babel

module.exports={

    entry:'./js/index.js',

    output:{

        path:__dirname,

        filename:'bundle.js'

},

devtool:'source-map',

module:{

    loaders:[

        {test:/\.js$/,loader:'babel'},

        {test:/\.css$/,loader:'style!css'} //匹配已 .css 为扩展名的文件

]

}

}

六、安装webpack-dev-server 模块热替换功能

  1. npm install webpack-dev-server - -global 先全局安装
  2. npm install webpack-dev-server - -save-dev 在安装到项目依赖中
  3. webpack-dev-server --inline --hot 

注意:默认生成服务器:localhost:8080,可以在devServer中更改。每次改代码保存后,会自动编译并且刷新页面。还可以做到热替换即不刷新页面可以更新。除此之外还有一个proxy功能可以用来配置代理。

    4.WDS 端口号等配置相关(主要还是用在开发环境,生产环境还是用其他的server的软件即可)
在 module.exports 里添加
devServer:{

    host:默认是localhost   也可以写自己的域名
    port:nodejs默认是8080 也可以写80或其他  在window系统可能在命令行前添加 sodu

}
webpack详细项目配置实用教程
注意:HMR在webpack官方给出了接口用于加载对应模块时 执行的业务逻辑进行局部刷新提高体验度
webpack详细项目配置实用教程

 

七、安装webpack插件webpack.optimize.uglifyJsPlugin()对打包的文件进行压缩

webpack详细项目配置实用教程
在module节点里加入以上 代码 可以 监控文件大小

var path=require('path');

var webpack=require('webpack');

module.exports={

entry: {

  bundle1: './main1.js', //入口1

  bundle2: './main2.js'//入口2

},

output: {

path:__dirname, //(如果指定路径必须是绝对路径)

path:path.join(_dirname,'dist') ,

publicPath:'/dist/' //在对应的html中引入 dist 目录下对应的打包文件

filename: '[name].bundle.js'  // [name]是一个变量,会自动替换成entry的key

 },

devtool:'source-map', //便于debugger调试,否则在浏览器的source中看不到自己的代码都是webpack的代码不利于调试

plugins:[

new webpack.optimize.uglifyJsPlugin() //对所有打包的文件进行压缩

new babili-webpack-plugin();  也可以对文件进行压缩   

],

module:{

loaders:[

{test:/\.js$/,loader:'babel'},

{test:/\.css$/,loader:'style!css'} //匹配已 .css 为扩展名的文件

]

}

}

plugins:的值是一个数组,所有插件都通过npm install进行安装,然后在plugins数组中添加对应的插件配置。有三个插件需要提一下:

1. HtmlwebpackPlugin 这个插件可以把生成的css ,js 插入到html页中的head,body中,这对于加了hash值的输出很有用。这个功能也可以用gulp的insject插件做。不过既然用webpack了,就暂时忘了gulp吧。在具有相似功能的不同的工具之间切换,并不是一个好主意。不过html这款插件有一个小小的问题,它对html中的img不会像css中那样解析。造成dist目录下的html文件,img下的src报错。解决办法是添加html-withimg-loader这个插件。

1

2

3

4

{

     test:/.html$/,

     loader:'html-withimg-loader?min=false'

 },

2. CommonsChunkPlugin 提取公共代码,这个不需要安装,webpack集成有。

1

2

3

4

5

new webpack.optimize.CommonsChunkPlugin({

            name:'vendor',

            filename:'js/vendor.js',

            chunks:['chunk1','chunk2','chunk3']//不写为所有的chunk,

        }),

chunk (块), webpack中另一个非常重要的概念,和entry对应。有三种情况:

2.1  如果entry通过字符串的方式指定的入口文件,那么chunk就是指入口文件,比如entry: './app.js'; 那么可以肯定chunk和'./app.js'一一对应。

2.2 如果是entry:['./app1.js','app2.js']那么chunk就是这两个文件之和。

     *  以上chunk的[name]就是默认的"main".

2.3  如果是下面这种形式:

1

2

3

4

5

entry:{

   index:'./index.js',

   error:'./error.js',

   vendor:['react','redux']

}

那么就会产生多个chunk,[name]分别和index,error,vendor对应。

3. ExtractTextPlugin 这个插件就是开头提到的,从html中分离css的插件。npm install extract-text-webpack-plugin --save-dev

1

2

3

plugins: [

        new ExtractTextPlugin("[name].css"),

]

webpack详细项目配置实用教程

需要注意的是,如果在[name].css前面加了子路径,如css/[name].css  那么就要小心样式中的图片路径出错,特别是在没有指定publicPah的情况下。background:url(这个地方的图片默认是和chunk的输出路径同级的,如果指定了publicPath,则以publicPath代替,不存在这个问题),但是由于我们人为的指定了打包后的样式放在css/目录下,而图片默认还在原来的目录,这就导致css中引用的图片路径失效。看下面的例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

var ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {

  entry: ['./main.js','./icon.css'],

  output: {

    path:'./dist',

    //publicPath:'./dist/',

    filename: 'bundle_[hash:8].js'

  },

  module: {

    loaders:[

        { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'},

        { test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css') },

    ]

  },

  plugins: [

    new ExtractTextPlugin("css/[name].css"),

  ]

};

webpack详细项目配置实用教程

从图上看到,添了css子目录之后,样式中的图片,还是原来的路径,而图片并不存在css目录下。

解决办法,要么不加子目录,保持样式文件和图片文件始终在同一层级,要么添加publicPath,相当于使用绝对路径。由于这个是在webpack.config.js中配置的,要更换也很容易。

看起来一切都很美好,可是当我们的html中也用了img标签的时候,问题就来了,还记得html-withimg-loader这个插件吗?它际实上也是调用的url-loader,所以,它最终的图片输出路径也样受publicPath的影响。考虑一下这样的目录结构:

webpack详细项目配置实用教程

样式文件是位于css子目录,而html则是和图片保持同级的。样式中的图片需要指定为"../",而html中的图片需要指定成"./",这在同一个publicPath中,显示是冲突的。这时就需要权衡一下,要么所有的文件都堆在根目录下,要么html中的图片用别的插件进行处理。总之,不能让这种相冲突的情况发生。

最后再简单说一下webpack.config.js 中的 resolve;

1

2

3

resolve: {

    extensions: ['', '.js', '.vue', '.json']

  },

通常我们都知道通过配置这个属性下的extensions,可以省略扩展名,似乎没有什么可以介绍的,直到有一次我在项目中通过npm install vue --save 安装了vue ,然后我在代码中用import Vue from 'vue' 导入了vue。到这一步都是正常的,可是当我打算进一步使用vue的时候,代码就报错了,然后去查官方文档(出错的时候不要惊慌,大多数情况都会在官方找到解决的办法,比如github中的issue等)。官方介绍如下

“ There are two builds available, the standalone build and the runtime-only build. The difference being that the former includes the template compiler and the latter does not.By default, the NPM package exports the runtime-only build. To use the standalone build, add the following alias to your Webpack config:”

1

2

3

4

5

resolve: {

    alias: {

      'vue$': 'vue/dist/vue.common.js'

    }

  },

大意是说:vue有两种构建方式,独立构建和运行构建。它们的区别在于前者包含模板编译器而后者不包含。而默认 NPM 包导出的是 运行时 构建。为了使用独立构建,要在 webpack 配置中添加下面的别名:添加alias:{'vue$':'vue/dist/vue.common.js'}. 这个别名,同样适用于jquery,zepto这些库。

对于vue来说,如果不用别名,也可以从node_modules/vue/dist/复制vue.common.js到开发目录下,比如./src/vue下面,然后像普通的js文件一样引用, import vue from './src/js/vue.common.js' 这也是可以的。只是和使用别名相比,显的很lower 

八、优化打包速度 noParse

它的值是个数组,每个元素都是一个正则表达式;

也可以在exclude下边添加 include属性 值也是数组里边是正则表达式(正则写需要babel转换的文件,也就是用es6写的js)

   1.  exclude是排除的目录,比如node_modules中的文件,通常都是编译好的js,可以直接加载,因此为了优化打包速度,可以排除。作为优化手 段它不是必须的。

   2. loader: 加载器的名称,每一个加载器都有属于它自己的用法,具体要参考官方说明。

   3. query: 传递给加载器的附加参数或配置信息,有些也可以通过在根目录下生成特殊的文件来单独配置,比如.babelrc

test: /\.js[x]?$/,

        exclude: /node_modules/,

        loader: 'babel-loader',

        query:{

           presets:['react','es2015']

        }

九、ESlint实现代码规范自动化测试    
目的:提高个人代码的质量,提高团队编码风格的统一性,可用性的保障,以及性能优化(先不详细写了)

  1. 首先在项目下安装 npm install eslint --save-dev

    然后在 package.json 文件  scripts中配置eslint
    app/   代表编译app目录下的文件
    webpack.*.js   --cahe  对webpack.*.js相关文件进行缓存
    webpack详细项目配置实用教程

    然后在配置.eslintrc.js  如下图   不懂的配置规则请移步到官网   https://eslint.org/docs/rules/

webpack详细项目配置实用教程
然后在 命令行中 执行 npm run eslint 就可以对代码进行编辑检测,但是有错误总是手动去改成就会浪费人工时间,这时在1.4 之后的版本 使用 npm run eslint -- --fix 可以自动帮助我们进行修正;
例如 :代码结尾 不加 “分号”  ,字符串用的“双引号“而不是“单引号“  等等问题都会自动修正提高代码质量;
    2:在webpack中配置 eslint
首先 安装 npm install eslint-loader --save-dev 
之后再webpack.config.js中配置 eslint-loader 
module:{

    rules:[

        {                                               

        test:/\.js$/,
        enfore:'pre',
        loader:'eslint-loader',

        options:{

            emitWarning:true

        },

        },

]}

然后在运行之前配置好的 npm run  start会发现跟之前不同的是webpack在编译之前会先对代码进行检测;
另附(根据个人喜好选择):
可以配置在浏览器中显示 检测出来的错误 或者 警告  便于调试
在之前 webpack.config.js 文件中 配置好的devServer中添加配置项 overlay
webpack详细项目配置实用教程

十、分离项目打包代码和组件代码  (重点)


一、没分离打包时的大小
webpack详细项目配置实用教程

  1. 如何分离
    webpack详细项目配置实用教程
    然后 npm  run build 对项目进行打包
    webpack详细项目配置实用教程
    会发现多了一个vendor.js 说明已把app.js进行了分离 大小也从175KB 变成 45.1KB
    webpack详细项目配置实用教程
    以上的图 左边是app.js 右边是vendor.js 他们是存在着公共部分代码的 如 A  和 下边节点B C
    所以进行压缩合并配置  如下:
  2. 对公共部分代码进行压缩合并
    在webpack.config.js文件中    如果没导先导 webpack  const webpack=require('webpack');
    然后在plugins  节点下配置 下图标注的部分这个插件是webpack自带的;
    webpack详细项目配置实用教程
    合并完之后app  和 vendor 公共部分进行了合并 如 下图
    webpack详细项目配置实用教程
    然后在进行 npm run build 打包
    webpack详细项目配置实用教程
    会发现上次分离之后打包app文件的45KB 在进行合并打包之后
    变成了 613JB    vendor.js变成46KB       再一次进行了优化
  3. 需要注意的是 分离出来的文件vendor.js 要在html 引入进去并且 放在 bundle.js /app.js上边

 

webpack详细项目配置实用教程
十一、使用可视化图表进行统计分析打包过程  (自选)
目的:对后期性能优化做准备,方便找出某些组件之间的依赖关系以及大小,监控编译所占用的时间等等;

webpack详细项目配置实用教程
然后运行 npm run stats   回生成 stats.json
webpack详细项目配置实用教程
这里有可视化图表相关内容
http://blog.parryqiu.com/2017/06/16/webpack2-Statistics/