遇见Vue.js

  • 关键词:Vue.js – 读书笔记

二十二、Webpack

基本概念

Webpack是一个模块化加载器,它同时支持AMD、CMD等加载规范。与其他模块化加载器相比,它具有以下优势:

1.代码分割

同步和异步,同步的依赖会在编译时直接打包输出到目的文件中;异步的依赖会单独生成一个代码块,只有在浏览器中运行需要的时候才会异步加载该代码块。

2.Loaders

在默认情况下,Webpack只处理js文件,但是通过加载器我们可以将其他类型的资源转换为js输出。

3.插件机制

Webpack提供了强大的插件系统,当Webpack内置的功能不能满足我们的构建需求时,我们可以通过使用插件来提高工作效率。

安装

全局安装:npm i webpack -g

作为依赖在项目中进行安装:

npm init

npm -i webpack –save-dev

基本使用

打包实例

    example
        |-app.js
        |-cats.js
    //cats.js
    var cats = ['dave', 'henry', 'martha'];
    module.exports = cats;
    //app.js
    cats = require('./cats.js');
    console.log(cats);
    //通过webpack命令指定要打包的入口文件app.js和最终输出文件app.bundle.js来打包应用
    webpack ./app.js app.bundle.js
    //我们在node环境中进行了演示,实际上webpack打包后的代码可以运行在任何环境中,包括浏览器环境

image

命令行

webpack命令进行简单打包,语法如下:

webpack

常用参数

entry为要打包的入口文件路径,output为打包后的文件路径。

-p:对打包后的代码进行压缩

–watch:文件发生变化时,重新打包

–config:指定webpack打包配置文件,稍后会详细介绍配置文件

–progress:在终端显示打包过程

配置文件

通过webpack命令行传参,我们可以进行简单的打包构建。对于复杂的打包,我们可以在项目的根目录下提供一个配置文件,在配置文件中对打包过程进行更详细的配置。在项目根目录下不提供参数直接调用webpack命令:

webpack

webpack默认会调用项目根目录下的webpack.config.js文件,我们也可以通过-config参数指定配置文件,执行如下命令:

webpack -config webpack.config.build.js

配置文件的内容需要通过module.exports进行导出:

//webpack.config.js

module.exports = { //配置选项 }

webpack中包含的配置选项

image

1.context,配置基础路径(必须为绝对路径),默认为process.cwd(),即运行webpack命令的目录。

2.entry,配置要打包的入口文件,值可以是字符串、数组、对象。该选项指定的路径会相对context选项指定的路径进行查找。

    //1.字符串
    //直接指定路径,该路径相对于context选项
    entry: "./entry"
    //2.数组
    //路径数组,webpack会按序打包,但是只导出最后一个文件
    entry: ["./entry1", "./entry2"]
    //3.对象
    //当entry值为对象时,键名为块名,可以随意指定,键值可以为字符串或数组类型。该块名可以在output选项中使用,代码示例:
    {
        entry: {
            page1: "./page1",
            page2: ["./entry1", "./entry2"]
        },
        output: {
            //打包后输出文件名,name为entry中对应的键名
            filename: "[name].bundle.js"
        }
    }
    //以上选项配置后,运行命令在项目根目录下回生成page1.bundle.js和page2.bundle.js文件

3.output,配置输出信息。

配置打包后的文件名,注意值不是绝对路径。我们应该通过output.path来指定输出路径,filename会相对output.path来输出。

    //单入口示例
    {
        entry: './src/app.js',
        output: {
            filename: 'bundle.js',
            path: _dirname + '/build'
        }
    }
    //写入磁盘路径为:./build/bundle.js

如果项目有多个入口,对于每个入口打包后的文件名我们需要保证其唯一性。webpack提供了一下模式来动态生成输出文件名:

[name],入口文件块名

[hash],每个入口打包后的hash值

[chunkhash],在使用代码分割时,异步加载的文件的hash值

    //多入口示例
    {
        entry: {
            app: './src/app.js',
            search: './src/search.js'
        },
        output: {
            filename: '[name].js',
            //filename: '[hash].js',
            //filename: '[chunkhash].js',
            path: _dirname + '/build'
        }
    }
    //写入磁盘路径为:./build/app.js、./build/search.js

output.path——打包后的文件的根目录(绝对路径)。

4.module,用来进行模块加载相关配置。

module.loaders——加载器数组,当依赖文件匹配指定的test模式时,webpack会自动调用数组中的相应加载器去处理该文件,然后返回js格式的文件。

加载器是一个对象,该对象拥有以下属性:

test:正则表达式,webpack用其去匹配相应的文件,通常用来匹配文件后缀。

exclude:不应该被loader处理的文件。

include:一个路径数组,这些路径将会被loader处理。

loader:test匹配到的文件对于的加载器,值是一个加载器名字字符串,多个加载器之间用”!”分隔。

代码示例如下:

    module: {
        loaders: [
            {
                //匹配jsx后缀的文件
                test: /\.jsx$/,
                //include中的目录会被loader解析
                include: [
                    path.resolve(__dirname, "app/src"),
                    path.resolve(__dirname, "app/test"),
                ],
                //babel loader,该loader可以用来解析ES 6语法
                loader: "babel-loader" //或者"babel",webpack将会自动添加'-loader'
            }
        ]
    }

5.resolve,用来配置依赖文件的匹配,如依赖文件别名配置、模块的查找目录、默认查找的文件后缀等。

resolve.alias:用来配置依赖文件的别名,值是一个对象,该对象的键是别名,值是实际路径。

resolve.root:用来指定模块的查找根路径,必须为绝对路径,值可以是路径字符串或者路径数组。若是路径数组,webpack会依次在这些路径中查找,如果找到则终止;否则会继续在下一个路径中查找。

resolve.modulesDirectories:用来指定模块目录,值是一个路径数组,默认值为[“web_modules”, “node_modules”]。

6.devServer,用来配置webpack-dev-server的行为。

    //以下代码用来指定服务的根路径
    {
        devServer: {
            contentBase: "./build"
        }
    }

开发调试

我们可以使用webpack-dev-server在浏览器中进行调试。

webpack-dev-server是一个基于Express的Node.js服务器。在文件发生改变时,它会自动触发打包过程,然后通过socket.io通知浏览器刷新页面,可以大大提高工作效率。

安装:npm i -g webpack-dev-server

启动服务: webpack-dev-server

不带参数运行以上命令,默认会读取webpack.config.js进行打包,我们可以通过-config来指定配置文件。

命令行参数

所有的webpack命令接受的参数,webpack-dev-server都可以接受。除此之外,我们还可以向webpack-dev-server传递额外的参数。

–content-base:指定请求的根路径

–host:指定服务端监听的地址,可以是IP地址或者域名

–port:指定服务器监听的端口号

–compress:启用gzip压缩

–inline:自动将Socket.io代码注入到打包后的文件中。启用该选项,当文件内容改变时可以自动刷新浏览器

除了通过命令行传参来配置webpack-dev-server外,我们还可以通过webpack配置文件,如默认的webpack.config.js中的devServer选项对其进行配置,所有命令行参数都支持在配置文件中进行设定。

使用插件

webpack允许我们使用插件来控制打包的各个过程。

安装

WebpackBrowserPlugin为例,该插件用来在Webpack或webpack-dev-server运行完成后启动浏览器。

npm install –save-dev webpack-browser-plugin

然后在webpack.config.js中引用插件,并在插件选项中注册该插件:

    //webpack.config.js
    var WebpackBrowserPlugin = require('webpack-browser-plugin');
    module.exports = {
        ...
        ...
        plugins: [
            new WebpackBrowserPlugin()
        ],
        ...
    }

如果是webpack内置的插件,首先需要在项目中安装webpack,执行如下命令:

npm install –save-dev webpack

然后在配置文件中引用并注册插件。以DefinePlugin为例,该插件可以在打包时替换指定变量:

    //webpack.config.js
    //首先需要引入Webpack
    var webpack = require('webpack');
    module.exports = {
        ...
        ...
        plugins: [
            //注册webpack内置插件
            new webpack.DefinePlugin({
                VERSION: JSON.stringify("5fa3b9"),
                BROWSER_SUPPORT_HTML5: true,
                TWO: "1+1",
                "typeof window": JSON.stringify("object")
            })
        ],
        ...
    }

常用插件

DefinePlugin

插件用来替换指定变量

ProvidePlugin

可以自动加载当前模块依赖的其他模块并以指定别名注入到当前模块中。

假如当前模块依赖jquery模块,同时我们想在模块中直接用”$”引用jQuery对象,但是不想手动require jquery模块。

    //webpack.config.js
    var webpack = require('webpack');
    module.exports = {
        ...
        ...
        plugins: [
            //自动引入jquery模块并导出为$变量,使各个模块可以直接通过"$"来引用jquery对象
            new webpack.ProvidePlugin({
                $: "jquery"
            })
        ],
        ...
    }

二十三、Rollup

“Rollup针对非JS资源的插件生态不行,也没有热替换,不适合用在应用层。所以用webpack做应用层开发,用Rollup做库的打包。”

开发一个项目时,我们会将项目拆分成多个模块,每个模块完成相对独立的功能。

这种情况下,很可能存在这种情况:项目依赖很多第三方组件,依赖组件都很小,这对于浏览器来说是非常糟糕的,增加了许多请求;对于前端开发来说,严重影响了页面加载速度。

针对上面情况:

采用模块化开发,使用模块化打包工具将所有文件最终打包到一个单独的输出文件中,大大减少了请求的数量。Browserify和Webpack就是这样的打包工具。

    //打包工具很快、很好、很方便。但是要注意以下问题:
    var utils = require('utils');
    var query = 'Rollup';
    utils.ajax('http:...?search = ' + query).then(handleResponse);
    //以上传统的打包方式是将所有代码全部打包,造成代码冗余
    //ES 6解决了这个问题,取代了引入全部工具函数,可以只引入所要使用的ajax方法
    import { ajax } from 'utils';
    var query = 'Rollup';
    ajax('http:...?search = ' + query).then(handleResponse);
    //很明显地体会到Tree-shaking的作用——bundle文件中只保留了utils模块里的ajax方法

Tree-shaking会抽取引用到的模块内容,将它们置于同一个作用域下,进而直接使用变量名就可以访问各个模块的接口;而不像webpack那样在每个模块外还要包一层函数定义,再通过合并进去的define/require相互调用。

Rollup是下一代ES 6模块打包工具。它采用Tree-shaking技术,利用ES 6模块静态分析语法树的特性,只将需要的代码提取出来打包,大大减小了代码体积。

安装

npm install -g rollup(npm run script环境中使用 npm I -D rollup,作为每一个项目的依赖)

配置

大多数选项都可以通过命令行直接指定,但是我们希望使用插件的配置文件,或者想以编程方式设置选项。

配置文件本身只是一个javascript模块(require和module.exports):

    import buble from 'rollup-plugin-buble';
    export default {
        entry: 'src/main.js',
        dest: 'dist/bundle.js',
        format: 'umd',
        plugins: [ buble() ]
    };
    //将会输出umd格式的内容到dist/bundle.js文件中
    //对于命令行实现上面例子中的配置输出,则可以在命令行中执行如下命令:
    -c-f umd -o dist/bundle.cjs.js

Rollup允许一个配置文件可以有多个目标文件,我们可以生产umd格式、ES格式的文件,都是来自同一文件,我们无须重复工作。

代码示例如下:

image

结果目录如下:

image

命令

在命令行中,运行rollup -h或者rollup -help查看命令。

rollup [options] <entry file>

其他命令选项和说明

(-v) 版本号

(-c) 配置文件(默认为roll.config.js),或者另有其名时,rollup -c rollup.config.dev.js

(-w) 监控文件变化后重新渲染文件

(-i) 全称input

(-o) 全称output

(-f) 格式化生成的包文件,全称:–format,支持以下参数:amd-异步模块定义(目前有两个JavaScript实现了AMD规范,require.js和curl.js)、cjs(CommonJS规范)、es6(默认)、iile(自执行,放入script标签中)、umd(通用模块定义)

(-e) 全称external,扩展插件。数组[Array]-外部依赖模块包的ID列表,ID可以是:外部依赖项的名称,resolved ID(例如一个文件的绝对路径)

(-g) 全称:–globals,全局属性,对UMD/IIFE模块有用

(-n) 全称:–name,UMD/IIFE模块的名称

(-u) 全称:–id,UMD/IIFE模块的id

(-m) 全称:–sourcemap,产生sourcemap

(–no-strict) 禁止生产“use strict”格式的代码

(–no-indent) 禁止缩进

(–environment) 设置传递给配置文件,如果使用--environment选项,通过process.env参数调用

(–no-conflict) 生产UMD全局noConflict方法

(–intro) 在bundle文件最前面插入内容

(–outro) 在bundle文件最后面插入内容

(–banner) 在bundle文件最前面插入内容

(–footer) 在bundle文件最后面插入内容

插件

Rollup也支持使用插件,写到配置对象的plugin中即可。这里以rollup-plugin-babel为例:

    import babel from 'rollup-plugin-babel';
    export default {
        entry: 'src/main.js',
        format: 'cjs',
        plugins: [ babel() ],
        dest: 'rel/bundle.js'
    };

与webpack不同的是,babel的预设不像webpack那样可以直接写在配置文件中,而是独立写个’src/.babelrc’。

在使用babel插件前,首先确保安装了rollup-plugin-babel和babel预设babel-preset-es2015-rullup: npm i rollup-plugin-babel babel-preset-es2015-rollup

这时候就能配合babel把ES 6模块编译成ES 5的bundle了。

其他常见插件列表:

image image