webpack学习

什么是webpack

webpack是一个JavaScript应用的静态模块打包工具。

需要关注的是模块打包grunt/gulp都可以打包,那有什么区别。

  • 模块化

    webpack可以支持前端模块化的一些方案,例如AMD、CMD、CommonJS、ES6。可以处理模块之间的依赖关系。不仅仅是js文件可以模块化,图片、css、json文件等等都可以模块化。

  • 打包

    webpack可以将模块资源打包成一个或者多个包,并且在打包过程中可以处理资源,例如压缩图片,将scss转成css,ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。grunt/gulp也可以打包。

和grunt/gulp的对比

  • grunt/gulp的核心是Task

    • 我们可以配置一系列的task,并且定义task要处理的事务(例如ES6/TS转化,图片压缩,scss转css)
    • 之后可以让grunt/gulp来执行依次这些任务,让整个流程自动化
    • 所以grunt/gulp也被称为前端自动化任务管理工具
  • 看一个gulp例子

    • task将src下的js文件转化为ES5语法

    • 并输入到dist文件夹中

      1
      2
      3
      4
      5
      6
      7
      8
      9
      const gulp = require('gulp')
      const babel = require('gulp-babel')
      gulp.task('js'()=>
      gulp.src('src/*.js')
      .pipe(babel({
      presets:['es2015']
      }))
      .pipe(gulp.dest('dist'))
      );
  • 什么时候使用grunt/gulp呢?

    • 如果工程依赖简单,甚至没有模块化
    • 只需要进行简单的合并/压缩
    • 如果模块复杂,相互依赖性强,我们需要使用webpack
  • grunt/glup和webpack区别

    • grunt/glup更加强调的是前端自动化流程,模块化不是其核心
    • webpack加强模块化开发管理,而文件压缩/合并/预处理等功能,是附带功能

webpack就是前端模块化打包工具

webpack的安装

  1. webpack依赖node环境。
  2. node环境依赖众多包,所以需要npm,npm(node packages manager)node包管理工具
  3. nvm是node管理工具可以自由切换node环境版本

全局安装webpack

1
2
3
npm install webpack -g
//指定版本安装
npm install webpack@3.6.0 -g

由于vue-cli2基于webpack3.6.0 如果要用vue-cli2的可以使用npm install webpack@3.6.0 -g

局部安装

1
npm install webpack --save-dev
  • 在终端执行webpack命令,使用的是全局安装。
  • 当在package.json中定义了scripts时,其中包括了webpack命令,那么使用的是局部webpack

简单命令

新建一个文件夹,新建如下结构的目录:

1
2
3
4
5
├── dist(要发布的文件,已经处理过的)
├── src(源码文件夹)
│ ├── index.html
│ ├── main.js
└─── mathUtils.js

如上所示在src文件夹(源码文件夹),dist(要发布的文件,已经处理过的)。

新建入口js文件main.jsmathUtils.jsmain.js依赖mathUtils.js

1
2
3
4
5
6
7
8
9
10
//1.新建mathUtils.js,用CommonJs规范导出
function add(num1,num2) {
return num1+num2
}
function mul(num1,num2) {
return num1*num2
}
module.exports = {
add,mul
}
1
2
3
4
5
//2.新建入口js文件main.js 导入mathUtil.js文件,并调用
const {add,mul} = require("./mathUtils.js")

console.log(add(10,20))
console.log(mul(10,10))
  1. 使用webpack命令打包js文件

    注意:

    • webpack3使用命令:webpack ./src/main.js ./dist/bundle.js

    • webpack4使用命令: webpack ./src/main.js -o ./dist/bundle.js 要加 -o (我使用的是webpack4)

    • webpack4打包成功会报The 'mode' option has not been set的警告、不用管、意思时要在模块上使用、这里是单文件(解决办法: https://www.cnblogs.com/wendj/p/10069882.html)

      打包成功,查看dist文件夹下自动生成了一个bundle.js

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      //新建入口js文件main.js 导入mathUtil.js文件,并调用
      const {add,mul} = __webpack_require__(1)

      console.log(add(10,20))
      console.log(mul(10,10))

      /***/ }),
      /* 1 */
      /***/ (function(module, exports) {

      //1.新建mathUtils.js,用CommonJs规范导出
      function add(num1,num2) {
      return num1+num2
      }
      function mul(num1,num2) {
      return num1*num2
      }
      module.exports = {
      add,mul
      }

      内容很多,其中包含mathUtils.jsmain.js 内容,打包成功。

  2. 新建一个index.html文件,导入bundle.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>webpack入门</title>
    </head>
    <body>
    <!-- 3.新建一个indexhtml文件并使用 webpack ./src/main.js -o ./dist/bundle.js webpack4使用此命令 -->
    <!-- 4.引用webpack打包后的js文件 -->
    <script src="./dist/bundle.js"></script>
    </body>
    </html>

    可以成功打印、就说明成功了

  3. 新建一个info.js使用ES6的语法导出

    • info.js

      1
      2
      3
      4
      5
      //es6语法导出
      export default {
      name:'zzz',
      age:24,
      }
    • main.js导入info.js

      1
      2
      3
      4
      5
      //使用es6语法导入
      import info from './info.js'

      console.log(info.name)
      console.log(info.age)

      再次使用webpack ./src/main.js -o ./dist/bundle.js,重新打包

  4. 打开index.html测试

    总结: webpack可以帮我们打包js文件,只要指定入口文件(main.js)和输出的文件(bundle.js),不管是es6的模块化还是CommonJs的模块化,webpack都可以帮我们打包,还可以帮我们处理模块之间的依赖。

webpack配置

基本配置

如果每次都用webpack命令自己写入口文件和出口文件会很麻烦,此时我们可以使用webpack的配置。

准备工作:

新建一个文件夹,新建如下结构的目录:

1
2
3
4
5
├── dist(要发布的文件,已经处理过的)
├── src(源码文件夹)
│ ├── main.js
│ ├── mathUtils.js
└── └── index.html
  1. 在根目录下新建一个webpack.config.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //1.导入node的path包获取绝对路径,需要使用npm init初始化node包
    const path = require('path')

    //2.配置webpack的入口和出口
    module.exports = {
    entry: './src/main.js',//入口文件
    output:{
    path: path.resolve(__dirname, 'dist'),//动态获取打包后的文件路径,path.resolve拼接路径
    filename: 'bundle.js'//打包后的文件名
    }
    }
  2. 在根目录执行npm init初始化node包,因为配置文件中用到了node的path包

    1
    npm init 

    看着提示的文字来选择(我记得我只输入了项目名称的、其他都是回车)

    生成了package.jsonnode_modules

  3. 在根目录下载执行webpack打包

    1
    webpack

    这样入口和出口的配置已经配置完成了,只需要使用webpack命令就行了。

  4. 使用自定义脚本(script)启动

    一般来是我们使用的是:

    1
    2
    npm run dev//开发环境
    npm run build//生产环境

    package.json中的script中加上

    1
    "build": "webpack"

    使用npm run build

    1
    npm run build

全局安装和局部安装

webpack有 全局安装 和 局部安装

  • 全局安装

    @xxx为对应版本

    1
    npm install webpack@xxx -g
  • 局部安装

    使用npm run build执行webpack会先从本地查找是否有webpack,如果没有会使用全局的。

    此时本地需要安装webapck

    1
    npm install webpack@xxx --save-dev

    package.json中自动加上开发时的依赖devDependencies

    1
    2
    3
    "devDependencies": {
    "webpack": "^xxx"
    }

    再次使用npm run build,使用的是本地webpack版本。

webpack的loader

loader是webpack中一个非常核心的概念。

webpack可以将js、图片、css处理打包,但是对于webpack本身是不能处理css、图片、ES6转ES5等。

此时就需要webpack的扩展,使用对应的loader就可以。

loader使用

  1. 通过npm安装需要使用的loader

  2. 通过webpack.config.js中的modules关键字下进行配置

大部分loader可以在webpack的官网找到对应的配置。

css文件处理

目录结构:

1
2
3
4
5
6
7
8
├── dist(要发布的文件,已经处理过的)
├── src(源码文件夹)
├── ├── js
│ └──── mathUtils.js
├── ├── css
│ └──── normal.css
│ ├── main.js
└── index.html
  1. 将除了入口文件(main.js)所有js文件放在js文件夹,新建一个css文件夹,新建一个normal.css文件

    1
    2
    3
    body{
    background-color: red;
    }
  2. main.js导入依赖

    1
    require('./css/normal.css')

    此时如果直接进行打包npm run build

    提示信息很清楚,打包到css文件时报错,提示我们可能需要一个loader来处理css文件。

  3. 安装css-loader

    1
    npm install --save-dev css-loader
  4. 使用css-loader

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    module.exports = {
    module: {
    rules: [
    {
    test: /\.css$/,//正则表达式匹配css文件
    //css-loader只负责css文件加载,不负责解析,要解析需要使用style-loader
    use: [{
    loader: 'css-loader'
    }]//使用loader
    }
    ]
    }
    }

    执行npm run build,提示打包成功,但是背景色并没有变红色,是因为css-loader只负责加载css文件,不负责解析,如果要将样式解析到dom元素中需要使用style-loader。

  5. 安装使用style-loader

    1
    npm install --save-dev style-loader
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    module: {
    rules: [
    {
    test: /\.css$/,//正则表达式匹配css文件
    //css-loader只负责css文件加载,不负责解析,要解析需要使用style-loader
    use: [{
    loader: 'style-loader'
    }, {
    loader: 'css-loader'
    }]//使用loader
    }
    ]
    }

    webpack使用多个loader是从右往左解析的,所以需要将css-loader放在style-loader右边先加载后解析

    此时样式成加载解析到DOM元素上。

less文件处理

  1. 在css文件夹中新增一个less文件(special.less)

    1
    2
    3
    4
    5
    6
    @fontSize:50px;//定义变量字体大小
    @fontColor:orange;//定义变量字体颜色
    body{
    font-size: @fontSize;
    color: @fontColor;
    }
  2. main.js中导入less文件模块

    1
    2
    3
    4
    //依赖less文件
    require('./css/special.less')
    //向页面写入一些内容
    document.writeln("hello,zykj!")
  3. 安装使用less-loader

    1
    npm install --save-dev less-loader less

    webpack.config.js中使用less-loader

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    module: {
    rules: [
    {
    test: /\.less$/,//正则表达式匹配css文件
    //css-loader只负责css文件加载,不负责解析,要解析需要使用style-loader
    use: [{
    loader: 'style-loader'
    }, {
    loader: 'css-loader'
    }, {
    loader: 'less-loader'//less文件loader
    }]//使用loader
    }
    ]
    }
  4. 执行npm run build

    less文件生效了,字体是orange,大小为50px。

图片文件的处理

需要准备: 两张图片,图片大小为一张8KB以下(实际大小为5KB,名称为small.jpg),一张大于8KB(实际大小为10KB,名称为big.jpg),新建一个img文件夹将两张图片放入。

  1. 修改normal.css样式,先使用小图片作为背景

    1
    2
    3
    4
    body{
    /* background-color: red; */
    background: url("../img/small.jpg");
    }

    此时如果直接使用npm run build 直接打包会报错,因为css文件中引用了图片url,此时需要使用url-loader

  2. 安装使用url-loader处理图片

    url-loaderfile loader 一样工作,但如果文件小于限制,可以返回 data URL

    1
    npm install --save-dev url-loader

    配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    test: /\.(png|jpg|gif|jpeg)$/,//匹配png/jpg/gif格式图片
    use: [
    {
    loader: 'url-loader',
    options: {
    limit: 8192//图片小于8KB时候将图片转成base64字符串,大于8KB需要使用file-loader
    }
    }
    ]
    }
  3. 打包

    使用npm run build打包后,打开index.html

    小于limit大小的图片地址被编译成base64格式的字符串。

    此时修改css文件,使用big.jpg做背景。

    1
    2
    3
    4
    5
    body{
    /* background-color: red; */
    /* background: url("../img/small.jpg"); */
    background: url("../img/big.jpg");
    }

    再次打包,报错,提示未找到file-loader模块。

    因为大于limit的图片需要file-loader来打包。

  4. 安装使用file-loader处理图片

    1
    npm install --save-dev file-loader

    不需要配置,因为url-loader超过limit的图片会直接使用file-loader

    再次打包,没有报错,打包成功,但是图片未显示。

    • 当加载的图片大小小于limit,使用base64将图片编译成字符
    • 当加载的图片大小大于limit,使用file-loader模块直接将big.jpg直接打包到dist文件家,文件名会使用hash值防止重复。
    • 此时由于文件路径不对所以导致没有加载到图片
  5. 如何使用file-loader,指定路径

    修改output属性

    1
    2
    3
    4
    5
    output:{
    path: path.resolve(__dirname, 'dist'),//动态获取打包后的文件路径,path.resolve拼接路径
    filename: 'bundle.js',//打包后的文件名
    publicPath: 'dist/'
    },

    此时打包,图片正常显示

    注意:一般来说,index.html最终也会打包到dist文件夹下,所以,并不需要配置publicPath,如何打包index.html请看webpack处理.vue文件。

    file-loader打包后,使用hash值做文件名太长,此时可以使用options的一些配置。

    1
    2
    3
    4
    options: {
    limit: 8192,//图片小于8KB时候将图片转成base64字符串,大于8KB需要使用file-loader
    name: 'img/[name]-[hash:8].[ext]'//img表示文件父目录,[name]表示文件名,[hash:8]表示将hash截取8位,[ext]表示后缀
    }

    修改options,加上name属性,其中img表示文件父目录,[name]表示文件名,[hash:8]表示将hash截取8位[ext]表示后缀

    再次打包

ES6语法处理

webpack打包时候ES6语法没有打包成ES5语法,如果需要将ES6打包成ES5语法,那么就需要使用babel。直接使用babel对应的loader就可以了。

安装

1
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
test: /\.js$/,
//排除node模块的js和bower的js
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
//如果要使用@babel/preset-env这里需要在根目录新建一个babel的文件
// presets: ['@babel/preset-env']
//这里直接使用指定
presets: ['es2015']
}
}
}
  1. 如果要使用@babel/preset-env这里需要在根目录新建一个babel的文件

  2. exclude排除不需要打包的文件

webpack的vue

简单安装使用vue

如果需要使用vue,必须使用npm先安装vue。

1
npm install vue --save	

使用vue简单开发。

准备工作:在项目根目录执行npm install vue --save ,下载安装vue。

  1. 在入口文件main.js导入已安装的vue,并在index.html声明要挂载的div。在main.js加入以下代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //使用vue开发
    import Vue from 'vue'

    const app = new Vue({
    el: "#app",
    data: {
    message: "hello webpack and vue"
    }
    })

    修改index.html代码,添加

    1
    2
    3
    <div id="app">
    <h2>{{message}}</h2>
    </div>
  2. 再次打包npm run build后打开index.html

    发现message并没有正确显示,打开console发现vue报错。错误提示我们,正在使用runtime-only构建,不能将template模板编译。

    • runtime-only模式,代码中不可以有任何template,因为无法解析。

    • runtime-complier模式,代码中可以有template,因为complier可以用于编译template。

      在webpack中配置,设置指定使用runtime-complier模式。

      webpack.config.js配置下修改

      1
      2
      3
      4
      5
      6
      7
      resolve: {
      // alias:别名
      alias: {
      //指定vue使用vue.esm.js
      'vue$':'vue/dist/vue.esm.js'
      }
      }
  3. 重新打包,显示正确

如何分步抽取实现vue模块

  • 创建vue的template和el关系
  • el表示挂载DOM的挂载点
  • template里面的html将替换挂载点

一般我们使用vue会开发单页面富应用(single page application),只有一个index.html,而且index.html都是简单结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>webpack</title>
</head>
<body>
<div id="app">
</div>
<script src="./dist/bundle.js"></script>
</body>
</html>
  1. 第一次抽取,使用template替换<div id="app"></div>

    修改main.js的vue相关代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //使用vue开发
    import Vue from 'vue'

    new Vue({
    el: "#app",
    template:`
    <div>
    <h2>{{message}}</h2>
    <button @click='btnClick'>这是一个按钮</button>
    <h2>{{name}}</h2>
    </div>
    `,
    data: {
    message: "hello webpack and vue",
    name: 'zykj'
    },
    methods: {
    btnClick(){
    console.log("按钮被点击了")
    }
    },
    })

    使用template模板替换挂载的id为app的div元素,此时不需要修改html代码了,只需要写template。

    再次打包,显示成功。

  2. 第二次抽取,使用组件化思想替换template

    在vue目录下创建App.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <template>
    <div>
    {{message}}
    </div>
    </template>

    <script>
    export default {
    name: "App",
    data() {
    return {
    message: "Hello world!"
    }
    }
    }
    </script>

    <style scoped>

    </style>

    修改main.js,vue实例中注册组件,并使用组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //使用vue开发
    import Vue from 'vue'
    //导入封装vue的.vue文件
    import App from './vue/App.vue'

    new Vue({
    el: "#app",
    //使用组件
    template: '<App/>',
    components: {
    //注册局部组件
    App
    }
    })

    再次使用npm run build打包,打包成功,显示和使用template替换div一样。

  3. 第三次抽取组件对象,封装到新的js文件,并使用模块化导入main.js

    需要安装 vue-loader

    1
    npm install --save-dev vue-loader vue-template-compiler

    此处我的vue-loader是15.9.2、会报错,此时需要降低版本

    package.jsonwebpack3.x将其修改为13.0.0、webpack4.x将其修改为14.2.4(这就是在使用webpack4遇到的坑)

    1
    "vue-loader": "^14.2.4"

    重新安装版本

    1
    npm install

    配置vue-loader、在webpack.config.js module->rules中

    1
    2
    3
    4
    {
    test:/\.vue$/,
    use: ['vue-loader']
    }

    再次打包,打包成功,样式生效了。

  4. 组件化开发

    我们使用app.vue分离了模板、行为、样式,但是不可能所有的模板和样式都在一个vue文件内,所以要用组件化。

    在vue文件夹下新建一个Cpn.vue文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <template>
    <div>
    <h2 class='title'>{{name}}</h2>
    </div>
    </template>

    <script type="text/ecmascript-6">
    export default {
    name: "Cpn",
    data() {
    return {
    name: "组件名字是Cpn"
    };
    }
    };
    </script>

    <style scoped>
    .title {
    color: red;
    }
    </style>

    将Cpn.vue组件导入到App.vue

    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
    <template>
    <div>
    <h2 class='title'>{{message}}</h2>
    <button @click="btnClick">按钮</button>
    <h2>{{name}}</h2>
    <!-- 使用Cpn组件 -->
    <Cpn/>
    </div>
    </template>

    <script type="text/ecmascript-6">
    //导入Cpn组件
    import Cpn from './Cpn.vue'
    export default {
    name: "App",
    data() {
    return {
    message: "hello webpack",
    name: "zzz"
    };
    },
    methods: {
    btnclick() {}
    },
    components: {
    Cpn //注册Cpn组件
    }
    };
    </script>

    <style scoped>
    .title {
    color: green;
    }
    </style>

    再次打包,打开index.html,cpn组件的内容显示基于此,一个vue文件可以依赖导入很多vue文件,组成一个单页面富应用。

    • 如果你在使用ES6语法导入模块时候想要简写的时候,例如这样省略.vue后缀

      1
      import Cpn from './Cpn'

      可以在webpack.config.js中配置:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      resolve: {
      //导入模块简写省略指定后缀如 import App from './vue/App'
      extensions: ['.js', '.css', '.vue'],
      // alias:别名
      alias: {
      //指定vue使用vue.esm.js
      'vue$':'vue/dist/vue.esm.js'
      }
      }

webpack的plugin

plugin插件用于扩展webpack的功能的扩展,例如打包时候优化,文件压缩。

loader和plugin的区别

  • loader主要用于转化某些类型的模块,是一个转化器。

  • plugin主要是对webpack的本身的扩展,是一个扩展器。

plugin的使用过程

  • 步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要在安装)

  • 步骤二:在webpack.config.js中的plugins中配置插件。

添加版权的Plugin

BannerPlugin插件是属于webpack自带的插件可以添加版权信息。

自带的插件无需安装,直接配置。

先获取webpack的对象,在配置BannerPlugin插件。

1
2
3
4
5
6
7
8
9
//获取webpack
const webpack = require('webpack')
//2.配置plugins
module.exports = {
...
plugins:[
new webpack.BannerPlugin('最终解释权归zykj所有')
]
}

打包后,查看bundle.js第一行

1
/*! 最终解释权归zykj所有 */

多了一行我们自定义的版权声明注释。

打包html的plugin

之前我们的index.html文件都是存放在根目录下的。

在正式发布项目的时候发布的是dist文件夹的内容,但是dist文件夹是没有index.html文件的,那么打包就没有意义了。

所以我们需要将index.html也打包到dist文件夹中,这就需要使用HtmlWebpackPlugin插件了。

HtmlWebpackPlugin

自动生成一个index.html文件(指定模板)

将打包的js文件,自动同script标签插入到body中

首先需要安装HtmlWebpackPlugin插件

1
npm install html-webpack-plugin --save-dev	

使用插件,修改webpack.config.js文件中的plugins部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//获取htmlWebpackPlugin对象
const htmlWbepackPlugin = require('html-webpack-plugin')
//配置plugins
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js",
//定义图片路径
// publicPath: "dist/" // 不需要dist目录、需要注释
},
...
plugins:[
new webpack.BannerPlugin('最终解释权归zykj所有'),
new htmlWbepackPlugin({
// 选择根目录中的index.html作为模板
template: 'index.html'
})
]
}

注意:

  1. template表示根据哪个模板来生成index.html
  2. 需要删除output中添加的publicPath属性,否则插入的script标签的src可能有误

再次打包,打开dist文件夹,多了一个index.html

自动加入了script引入了bundle.js。

压缩打包代码插件

webpack3.x:

uglifyjs-webpack-plugin是第三方插件,如果是vuecli2需要指定版本1.1.1。

安装:

1
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev

配置plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
//获取uglifyjs-webpack-plugin对象
const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
//配置plugins
module.exports = {
...
plugins:[
new webpack.BannerPlugin('最终解释权归zykj所有'),
new htmlWbepackPlugin({
template: 'index.html'
}),
new uglifyjsWebpackPlugin()
]
}

打包过后,打开bundle.js,发现已经压缩了,此时版权声明被删除了。

webpack高版本:自带了压缩插件。

参考: webpack4 未设置mode会自动压缩

以webpack4.x为例、配置package.json

1
2
3
4
5
"scripts": {
...,
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
  • mode development: 不会自动压缩
  • mode production: 同样会调用UglifyjsPlugin插件、会自动压缩

运行 npm run dev不会自动压缩

webpack搭建本地服务器

webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用了express框架,可以实现热启动。

不过这是一个单独的模块,在webpack中使用之前需要先安装:

1
npm install --save-dev webpack-dev-server@2.9.1

devServer也是webpack中一个选项,选项本省可以设置一些属性:

  • contentBase:为哪个文件夹提供本地服务,默认是根文件夹,这里我们需要改成 ./dist
  • port:端口号
  • inline:页面实时刷新
  • historyApiFallback:在SPA(单页面富应用)页面中,依赖HTML5的history模式

修改webpack.config.js的文件配置

1
2
3
4
5
6
7
8
9
10
//配置webpack的入口和出口
module.exports = {
...
devServer: {
contentBase: './dist',//服务的文件夹
port: 4000,
inline: true//是否实时刷新
}

}

配置package.json的script:

1
"dev": "webpack-dev-server --open"
  • --open: 表示直接打开浏览器

启动服务器

1
npm run dev

启动成功,自动打开浏览器,发现在本地指定端口启动了,此时你修改src文件内容,会热修改。

  1. 服务器启动在内存中。

  2. 开发调试时候最好不要使用压缩js文件的插件,不易调试。

webpack的配置文件分离

webpack.config.js文件中有些是开发时候需要配置,有些事生产环境发布编译需要的配置,比如搭建本地服务器的devServer配置就是开发时配置,接下来我们分析如何分离配置文件。

目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
├── dist(要发布的文件,已经处理过的)
├── build
| ├─── base.config.js
| ├─── dev.config.js
| └─── prod.config.js
├── src(源码文件夹)
| ├── js
| └──── mathUtils.js
├────├── css
| └──── normal.css
| ├── main.js
└── index.html

在根目录下新建一个build的文件夹,新建配置文件。

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
//1.导入node的path包获取绝对路径,需要使用npm init初始化node包
const path = require('path')
//获取webpack
const webpack = require('webpack')
//获取htmlWebpackPlugin对象
const htmlWbepackPlugin = require('html-webpack-plugin')

//2.配置webpack的入口和出口
module.exports = {
entry: './src/main.js',//入口文件
output:{
path: path.resolve(__dirname, 'dist'),//动态获取打包后的文件路径,path.resolve拼接路径
filename: 'bundle.js',//打包后的文件名
// publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.css$/,//正则表达式匹配css文件
//css-loader只负责css文件加载,不负责解析,要解析需要使用style-loader
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}]//使用loader
},
{
test: /\.less$/,//正则表达式匹配css文件
//css-loader只负责css文件加载,不负责解析,要解析需要使用style-loader
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}, {
loader: 'less-loader'//less文件loader
}]//使用loader
},
{
test: /\.(png|jpg|gif)$/,//匹配png/jpg/gif格式图片
use: [
{
loader: 'url-loader',
options: {
limit: 8192,//图片小于8KB时候将图片转成base64字符串,大于8KB需要使用file-loader
name: 'img/[name].[hash:8].[ext]'//img表示文件父目录,[name]表示文件名,[hash:8]表示将hash截取8位[ext]表示后缀
}
}
]
},
{
test: /\.js$/,
//排除node模块的js和bower的js
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
//如果要使用@babel/preset-env这里需要在根目录新建一个babel的文件
// presets: ['@babel/preset-env']
//这里直接使用指定
presets: ['es2015']
}
}
},
{
test: /\.vue$/,//正则匹配.vue文件
use: {
loader: 'vue-loader'
}
}
]
},
resolve: {
// alias:别名
alias: {
//指定vue使用vue.esm.js
'vue$':'vue/dist/vue.esm.js'
}
},
plugins:[
new webpack.BannerPlugin('最终解释权归zz所有'),
new htmlWbepackPlugin({
template: 'index.html'
})
]
}
1
2
3
4
5
6
7
module.exports = {
devServer: {
contentBase: './dist',//服务的文件夹
port: 4000,
inline: true//是否实时刷新
}
}
1
2
3
4
5
6
7
const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
plugins:[
new uglifyjsWebpackPlugin()
]
}

此时我们将webpack.config.js文件分成了三个部分,公共部分、开发部分、构建发布的部分。

  1. 如果此时是dev环境,我们只需要使用base.config.js+dev.config.js的内容

  2. 如果此时是生产发布构建的环境,我们只需要使用base.config.js+prod.config.js的内容

要将两个文件内容合并需要使用webpack-merge插件,安装webpack-merge

1
npm install webpack-merge --save-dev

合并内容都是将base.config.js的内容合并到dev或者prod的文件中,修改dev.config.jsprod.config.js文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//导入webpack-merge对象
const webpackMerge = require('webpack-merge')
//导入base.config.js
const baseConfig = require('./base.config')

//使用webpackMerge将baseConfig和dev.config的内容合并
module.exports = webpackMerge(baseConfig, {
devServer: {
contentBase: './dist',//服务的文件夹
port: 4000,
inline: true//是否实时刷新
}

})
1
2
3
4
5
6
7
8
9
10
11
12
const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
//导入webpack-merge对象
const webpackMerge = require('webpack-merge')
//导入base.config.js
const baseConfig = require('./base.config')

//使用webpackMerge将baseConfig和prod.config的内容合并
module.exports = webpackMerge(baseConfig, {
plugins:[
new uglifyjsWebpackPlugin()
]
})

此时我们使用三个文件构成了配置文件,此时在不同环境使用不同的配置文件,但是webpack不知道我们新配置文件,此时我们需要在package.json中的script指定要使用的配置文件。

1
2
3
4
5
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config ./build/prod.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"
}

此时使用npm run build打包文件,dist文件并不在根目录下,因为我们在base.config.js中配置的出口文件使用的是当前文件的路径,即打包的根路径是配置文件的当前路径,也就是build文件夹。

1
2
3
4
5
6
entry: './src/main.js',//入口文件
output:{
path: path.resolve(__dirname, 'dist'),//动态获取打包后的文件路径,path.resolve拼接路径
filename: 'bundle.js',//打包后的文件名
// publicPath: 'dist/'
}

注意:__dirname是当前文件路径,path.resolve拼接路径,所以在当前路径下创建了一个dist文件夹。

此时修改output属性:

1
2
3
4
5
output:{
path: path.resolve(__dirname, '../dist'),//动态获取打包后的文件路径,path.resolve拼接路径
filename: 'bundle.js',//打包后的文件名
// publicPath: 'dist/'
}

使用../dist,在当前目录的上级目录创建dist文件夹