3

webpack.png

webpack基础

认识webpack

webpack是一个现代的JavaScript应用的静态模块打包工具。
webpack.png
涉及到两个概念:模块打包

模块

通过模块化开发完成项目后,还需要处理模块间的各种依赖,并且将其进行整合打包。而webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做模块来使用。这就是webpack中模块化的概念。

打包(bundle)

就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。

但打包的操作似乎grunt/gulp也可以帮助我们完成,它们有什么不同呢?

和grunt/gulp的对比

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

来看一个gulp的task:下面的task就是将src下面的所有js文件转成ES5的语法。并且最终输出到dist文件夹中。

const gu1p = requireC('gulp');
const babe1 = require('gu1p-babe1');
gulp.task('js',()=>
    gulp.src('src/*.js')
        .pipe(babe1({
            presets: ['es2015']
        }))
        .pipe(gulp. dest('dist'))
);

什么时候用grunt/gulp呢?
如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念。只需要进行简单的合并、压缩,就使用grunt/gulp即可。但是如果整个项目使用了模块化管理,而且相互依赖非常强,我们就可以使用更加强大的webpack了。
所以,grunt/gulp和webpack有什么不同呢?

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

webpack的安装

安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm。

cnpm install webpack --save-dev
//--save-dev是开发时依赖,项目打包后不需要继续使用的。

在终端直接执行webpack命令,使用的全局安装的webpack。当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack。

webpack的起步

文件和文件夹解析:
dist文件夹:用于存放之后打包的文件
src文件夹:用于存放我们写的源文件
-----main.js项目的入口 文件。具体内容查看wb下面详情。
index.html:浏览器打开展示的首页html
package.json:通过npm init生成的,npm包管理的文件(暂时没有用上,后面才会用上)

js文件的打包

webpack src/main.js dist/bundle.js

使用打包后的文件
打包后会在dist文件下,生成一个bundle.js文件。bundle.js文件,是webpack处理了项目直接文件依赖后生成的一个js文件,我们只需要将这个js文件在index.html中引入即可

webpack的配置

局部安装webpack

因为一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题。所以通常一个项目,都有自己局部的webpack。

//安装指定自己需要的版本
cnpm install webpack@3.6.0 --save-dev
//启动webpack打包
node_modules/.bin/webpack
package.json中定义启动

我们可以在package.json的scripts中定义自己的执行脚本。

{
    "name": "meetwebpack",
    "version": "1.0.0",
    "description":"",
    "main": 'index.js',
    "scripts": {
        "build": "'webpack"
    },
    "author":"",
    "license":"ISC",
    "devDependencies": {
        "webpack": "^3. 6. 0"
    }
}

package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。
首先,会寻找本地的node_modules/.bin路径中对应的命令。如果没有找到,会去全局的环境变量中寻找。

//执行build指令
cnpm run build

搭建本地服务器

webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。

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

devserver也是作为webpack中的一个选项,选项本身可以设置如下属性:

`contentBase`:为哪一个文件夹提供本地服务,默认是根文件夹,我们这里要填写./dist
`port`:端口号
`inline`:页面实时刷新
`historyApiFallback`:在SPA页面中,依赖HTML5的history模式
devServer: {
    contentBase: './dist',
    inline: true
}

可以再配置另外一个scripts:--open参数表示直接打开浏览器

"scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server --open"
},

入口和出口

如果每次使用webpack的命令都需要写上入口和出口作为参数,有一种方法可以将这两个参数写到配置中,在运行时,直接读取。

创建一个webpack.config.js文件
const path = require('path')
module.exports = {
    //入口:可以是字符串/数组/对象,这里我们入口只有一个,所以写一个字符串即可
    entry:'./src/main.js' ,
    //出口:通常是一个对象,里面至少包含两个重要属性,path 和filename
    output:{
        path: path. resolve(__dirname, 'dist'), //注意: path通常是一 个绝对路径
        filename: 'bundle.js'
    }
}

loader的使用

什么是loader?
loader是webpack中一个非常核心的概念。在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。对于webpack本身的能力来说,对于这些转化是不支持的。此时需要给webpack扩展对应的loader。
loader使用过程:
步骤一:通过npm安装需要使用的loader
步骤二:在webpack.config.js中的modules关键字下进行配置
大部分loader我们都可以在webpack的官网中找到,并且学习对应的用法。loader 的执行顺序是从下到上,从右到左。

css文件处理

需要用到两个loader:css-loaderstyle-loadercss-loader负责加载css文件;style-loader负责将css具体样式嵌入到文档中。
安装

cnpm install --save-dev css-loader style-loader

webpack.config.js中配置

const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module:{
        rules:[
            { 
                test:/\.css$/, 
                use:['style-loader','css-loader'], 
            }
        ]
    },

}

因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。所以style-loader需要放在css-loader的前面。

less/scss文件处理

需要安装less 和 less-loader

rules: [{
            test: /\.less$/,
            use: [{
                loader: "style-loader" // creates style nodes from JS strings
            }, {
                loader: "css-loader" // translates CSS into CommonJS
            }, {
                loader: "less-loader" // compiles Less to CSS
            }]
        }]

图片文件处理

url-loaderfile-loader
当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
当加载的图片, 大于limit时, 需要使用file-loader模块进行加载.
修改文件名称
当图片大于limit时,使用file-loader默认会将该图片打包重新命名为32位的hash值放到dist文件夹下。

{
    test: /\.(png|jpg|gif|jpeg)$/,
    use: [
      {
        loader: 'url-loader',
        options: {
          // 当加载的图片, 小于limit时, 会将图片编译成base64字符串形式.
          // 当加载的图片, 大于limit时, 需要使用file-loader模块进行加载.
          limit: 13000,
          // [ext]:表示原始文件名的后缀。[name]:表示原始文件名。[hash:8]:为了防止图片名称冲突,依然使用hash,但是我们只保留8位
          name: 'img/[name].[hash:8].[ext]'
        },
      }
    ]
},

我们发现图片并没有显示出来,这是因为图片使用的路径不正确。默认情况下,webpack会将生成的路径直接返回给使用者但是,我们整个程序是打包在dist文件夹下的,所以这里我们需要在路径下再添加一个dist/

output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: 'dist/'
},

babel 的使用

如果希望将ES6的语法转成ES5,那么就需要使用babel。而在webpack中,我们直接使用babel对应的loader就可以了。

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

配置webpack.config.js文件

{
    test: /\.js$/,
    // exclude: 排除
    // include: 包含
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['es2015']
      }
    }
}

plugin的使用

plugin是插件的意思,通常是用于对某个现有的架构进行扩展。webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。

loader和plugin区别

loader主要用于转换某些类型的模块,它是一个转换器
plugin是插件,它是对webpack本身的扩展,是一个扩展器

plugin的使用过程:

步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
步骤二:在webpack.config.js中的plugins中配置插件。

添加版权的Plugin

BannerPlugin,属于webpack自带的插件。

const path = require('path')
const webpack = require('webpack')

module.exports = {
  ...
  plugins: [
      new webpack.BannerPlugin('最终版权归aaa所有'),
  ],
}

打包html的plugin

在真实发布项目时,需要将index.html文件打包到dist文件夹中,这个时候就可以使用 HtmlWebpackPlugin插件。HtmlWebpackPlugin插件可以为我们做这些事情:自动生成一个index.html文件(可以指定模板来生成)将打包的js文件,自动通过script标签插入到body中。

npm install html-webpack-plugin --save-dev
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  ...
  plugins: [
      new HtmlWebpackPlugin({
        template: 'index.html'//指定生成Html文件时使用的模板。
      }),
  ],
}

另外,我们需要删除之前在output中添加的publicPath属性,否则插入的script标签中的src可能会有问题。

CleanWebpackPlugin

CleanWebpackPlugin 会在打包之前删除指定目录下的内容。

npm install clean-webpack-plugin -D

webpack.config.js

const {CleanWebpackPlugin} = require('clean-webpack-plugin'); //注意加{}
module.exports = {
    ...
    plugins:[
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }), 
        new CleanWebpackPlugin() //不能加参数。
    ],
}

性能优化

优化构建速度

优化babel-loader
  • 优化Loader的文件搜索范围
  • 把Babel编译过的文件缓存起来
module.exports = {
    module:{
        rules:[
          {
            test:/\.js$/,//js文件才使用babel
            loader:'babel-loader?cacheDirectory=ture',//下次只需要编译更改过的代码文件即可
            include:[resolve('src')],//只在src文件夹下查找
            exclude:/node_modules/,//不会去查找的路径
          }
        ]
    }
}
ParallelUgifyPlugin

使用ParallelUglifyPlugin开启多进程压缩JS文件

happyPack

loader以多进程去处理文件,借助缓存机制,可以在rebuild的时候更快

const path = require('path')
const HappyPack = require('happypack');

module.exports = {
    ...
    new HappyPack({
        id: 'jsx',
        threads: 4,
        loaders: [ 'babel-loader' ]
    }),
    new HappyPack({
        id: 'styles',
        threads: 2,
        loaders: [ 'style-loader', 'css-loader', 'less-loader' ]
    })
}
DllPlugin和DllReferencePlugin

DllPlugin可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。webpack 自带的库。使用DllPlugin减少基础模块编译次数。使用DllReferencePluhin将依赖文件引入项目中。

const path = require('path')
const webpack = require('webpack')

module.exports = {
    ...
    new webpack.DllPlugin({
        name:'[name]-[hash]',//name必须和output.library一致
        context:__dirname,//该属性需要与DllReferencePlugin中一致
        path:path.join(__dirname,'dist','[name]-mainfest.json')
   }),
   new webpack.DllReferencePlugin({
        context: __dirname,
        mainfest:require('./dist/vendor-mainfest.json')
   })
}
IgnorePlugin
noParse

优化开发体验

使用自动刷新
  • Webpack监听文件
  1. 启动webpack时加上--watch参数;
  2. 在配置文件中设置watch:true。
  • DevServer刷新浏览器

DevServer刷新浏览器有两种方式:

  1. 向网页中注入代理客户端代码,通过客户端发起刷新
  2. 向网页装入一个iframe,通过刷新iframe实现刷新效果
热更新

在文件发生改变的时候,不需要重启服务器即可生效。
开启方式:

  1. webpack-dev-server --hot
  2. 使用HotModuleReplacementPlugin
const path = require('path')
const webpack = require('webpack')

module.exports = {
    ...
    new webpack.HotModuleReplacementPlugin()
}

优化产出代码

  • 使用生产环境
  • 小图片base64编码
  • bundle 加 hash
  • 使用CDN
  • 提取公共改代码
  • 懒加载

webpack中配置Vue

el和template区别

在前面的Vue实例中,我们定义了el属性,用于和index.html中的#app进行绑定,让Vue实例之后可以管理它其中的内容。
这里,我们可以将div元素中的{{message}}内容删掉,只保留一个基本的id为div的元素
但是如果我依然希望在其中显示{{message}}的内容,应该怎么处理呢?
我们可以再定义一个template属性,代码如下:

new Vue({
    el: '#app',
    template: '<div id="app">{{message}}</div>',
    data: {
        message:'coderwhy'
    }
})

el和template模板的关系是什么呢?
el用于指定Vue要管理的DOM,可以帮助解析其中的指令、事件监听等等。
而如果Vue实例中同时指定了template,那么template模板的内容会替换掉挂载的对应el的模板。这样做之后我们就不需要在以后的开发中再次操作index.html,只需要在template中写入对应的标签即可

稍后会将template模板中的内容进行抽离。会分成三部分书写:templatescriptstyle,结构变得非常清晰。

.vue文件封装处理

main.js

//使用Vue进行开发
import Vue from 'vue'
import App from './vue/App.vue'

new Vue({
  el: '#app',
  template: '<App/>',
  components: {
    App
  }
})

vue-->App.vue

<template>
  <div>
    <h2 class="title">{{message}}</h2>
    <h2>{{name}}</h2>
  </div>
</template>

<script>
  export default {
    name: "App",
    data() {
      return {
        message: 'Hello Webpack',
        name: '张三'
      }
    },
  }
</script>

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

安装vue、vue-loader 和 vue-template-compiler 进行处理。

npm install vue-loader vue-template-compiler --save-dev
cnpm install vue --save
 module: {
    rules: [
      {
        test: /\.vue$/,
        use: ['vue-loader']
      }
    ]
},
//解决runtime-only版本的Vue报错
resolve: {
    // alias: 别名
    extensions: ['.js', '.css', '.vue'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
}

梁柱
135 声望12 粉丝

« 上一篇
3.Vue组件开发
下一篇 »
5.Vue-cli