webpack实践

前端工程化

Posted by Felix on September 2, 2018

webpack

什么是webpack?

webpack是模块打包机:分析你的项目结构,找到javascript模块以及一些其他 浏览器不能直接运行的拓展语言(scss、typescript)并将它们转换和打包成合适的格式供浏览器使用。

为什么用webpack?

工程化实践

命令行运行

webpack {entry file} {destination for bundled file}

配置webpack.config.js 1.package.json中的script会按照一定顺序查找命令行对应的位置,本地的node_modules/.bin在查找清单中,无论全局还是局部安装webpack都不需要写前面那指明详细的路径了; 2.npm start 执行对应命令;不是start,npm run {script name} 常见命令行:

$ npm install xxx  
## 利用npm安装模块到当前命令行所在目录
$ npm install xxx --save 
## "package.json"中的"dependencies"
$ npm install xxx -g 
## 利用npm安装全局模块
$ npm uninstall xxx ## 删除模块
$ npm uninstall xxx -g ## 删除全局模块
$ npm uninstall xxx --save-dev ## 删除模块并同步parkage.json文件中的"devdenpendencies"字段
$ npm uninstall xxx --save ## 删除模块并同步parkage.json中的"dependencies"字段

webpack --config webpack.conf.dev.js 指定配置文件启动,默认是webpack.config.js

Q:devDenpendencies 和dependencies的区别?

devDependencie是一个对象,配置模块依赖的模块列表,属于开发环境的依赖,比如测试或者文档框架;

Dependencies配置生产环境的依赖,程序正常运行时需要加载的依赖

常用的loader

loaders配置通常包含这几个方面:

module.exports = {
   module:{
       rules:[
          {
              test:RegExp,
              loader:[{}],
              include //exclude:手动添加必须处理的文件(文件夹)或者屏蔽不需要处理的文件,
              query:为loaders提供额外配置兼容性一般
          }
       ]
   }
}

css-loader

使你能够用类似@import url(…)方法实现require()功能

$ npm install css-loader --save-dev

style-loader

$ npm install style-loader --save-dev

将所有计算后的样式表嵌入webpack打包后的js文件中

style-loader css-loader 加载有先后顺序

webpack可以把以指定入口的一系列相互依赖的模块打包成一个文件,这里的模块指的不只是js,也可以是css。也就是说,当你用CommonJs规范去引用css文件时,webpack会把你引用的css文件也打包到最终的生成文件中里。这样我们如何让样式生效呢?有两种方法:一种是,在引入css时,在最后生成的js文件中进行处理,动态创建style标签,塞到head标签里。另一种就是把css样式提取出来单独打包。

vue-loader

stylus-loader

Stylus - 富有表现力的、动态的、健壮的CSS

安装:

$ npm install stylus stylus-loader --save-dev

配置:

// webpack.config.js
module.exports = {
    module:{
        rules:[
            {
                test:/\.styl$/,
                use:[
                    "style-loader",
                    'css-loader',
                    'stylus-loader'
                ]
            }
        ]
    }
}

post-loader

autoprefixer

postcss-loader 和 autoprefixe为css代码添加前缀;

$ npm install postcss-loader autoprefixer --save-dev
...
use:[
    ...
    {
        loader:"postcss-loader"
    }
]

项目目录下配置postcss.config.js

module.exports = {
    plugins:[
        require('autoprefixer')
    ]
}

编译es6

babel是一个javascript编译平台,可以实现 1.使用最新的js代码 而不用管新标准是否被当前浏览器完全支持; 2.使用基于js进行拓展的语言 比如jsx ts

项目常用组合:bable-core babel-preset-env babel-runtime babel-plugin-transform-runtime

babel-cli

Babel的CLI是一种在命令行下使用babel编译文件的简单方法

$ npm install --global babel-cli

开始编译文件

$ babel example.js

将编译结果输出到终端

$ babel example.js --out-file complied.js
$ babel example.js -o compiled.js

如果把一个目录整个编译到一个新的目录

$ babel src --out-dir lib
$ babel src -d lib

babel-core

用编程的方式使用babel,babel-core的作用是把js代码分析成ast(abstract syntax tree),方便插件分析语法进行处理。有些新语法在低版本 js 中是不存在的,如箭头函数,rest 参数,函数默认值等,这种语言层面的不兼容只能通过将代码转为 ast,分析其语法后再转为低版本 js。

首先安装 babel-core。.

$ npm install babel-core 

通过安装插件(plugins) 和 预设(presets 一组插件)来指示babel应该去做什么事情。

插件只是单一功能,比如:

  • es2015-arrow-functions
  • es2015-classes

安装箭头函数插件

$ npm install --save-dev babel-plugin-transform-es2015-arrow-functions

一个个安装太麻烦,我们将预设和插件写入配置文件,可以写入package.json 中babel属性 或者

babel-preset-env

可以根据声明的环境编译声明环境缺少的的特性代码

安装:

$ npm install babel-preset-env --save-dev

默认配置相当于 babel-preset-latest

babel-preset-latest 就是把所有es2015, es2016, es2017 全部包含在一起了。

babel-polyfill

污染全局变量,在global对象上挂载新特性对象,模拟es6环境,转码新增API。污染模块使用者的全局变量所以不能用于模块和库。

安装

$ npm install babel-polyfill --save 

在文件顶部引用

import "babel-polyfill"

babel-runtime babel-plugin-transform-runtime

局部垫片,不污染全局环境,引入帮助函数

安装:

$ npm install babel-plugin-transform-runtime --save-dev
$ npm install babel-runtime --save

配置.babelrc文件:


   {"presets":[["env",{
       "targets":{
            "browsers":[
                "last 2 versions""ie >= 7"
            ]
        }
    }]],
    "plugins":[
        "transform-runtime"
    ]
}
// 不能出现空格或者单引号,必须完全符合json规范

Q:babel-polyfill babel-runtime区别?

Babel-polypill全局垫片(理解为挂载在global对象上),污染全局变量,为应用准备

代码最前端 import “babel-polyfill” 随意使用;

Babel-runtime 局部垫片 为开发框架准备 相当于require(“babel-runtime/core-js/promise.js”)等帮助函数文件

babel-plugin-transfomr-runtime消除多余代码

编译typescript

js的超集

安装:

$ npm i typescipt ts-loader --save-dev   ## 官方
$ npm i typescipt awesome-typescript-loader ## 第三方

配置:

tsconfig.js. webpack.config.js

配置选项:

compilerOptions include exclude

 module:{
        rules:[
            {
                test:/\.tsx?$/,
                use:{
                    loader:'ts-loader'
                }
            }
        ]
    }

声明文件

npm install ## types/lodash --save
npm install 

提取公共代码

作用:减少代码冗余 提高用户的加载速度 ,将独立通用模块提取出来。

配置:

// webpack.config.js
var webpack = require('webpack)
...
entry: {
        // 定义多个入口文件
        'common': ['./src/page/common/index.js'],
        'index': ['./src/page/index/index.js'],
        'login': ['./src/page/login/index.js']
    },
plugins:[
    new webpack.CommonsChunkPlugin({
        name:'common', 
        filename:'js/base.js'
    })
]
// name可以是已经存在的chunk一般指入口文件对应的name,那么会把公共模块的代码合并到这个chunk上,否则会创建名字为name的common chunk进行合并;
// 给出一个数组,根据数组每一项新建插件实例多次
// filename 指定common chunk的文件名相对于webpackConfig.output.path的路径
// chunks 指定的source chunk 指定从哪些chunk当中寻找公共模块,省略该选项的时候默认就是entry chunks
// minChunks 可以是数字需要提取的公共代码最小次数 也可以是函数自定义提取代码逻辑 也可以是infinity不会打包任何模块
// children async属于异步中的应用
// deepChildren 查找共同依赖
// 将入口文件中的节点的打包后的公共部分分离到js/base.js中去,同时还包含common入口节点的所有内容

场景: 单页应用

单页应用 + 第三方依赖

多页应用 + 第三方依赖 + webpack生成代码

添加文件注释

const webpack = require('webpack')
plugins:[
       ...
    new webpack.BannerPlugin('版权所有,违者必究')
]

提取css文件

作用:将css文件从打包后的js文件提取

安装:

$ npm install extract-text-webpack-plugin --save-dev

配置:

// webpack.config.js
var ExtractTextPlugin = require('extract-text-webpack-plugin')
// 提取css
var extreactCss = new ExtractTextPlugin('css/[name].css')
// 提取 stylus
var extractStyl = new ExtractTextPlugin('styl/[name].css')
...
module:{
    rules:[
        
                test: /\.css$/,
                // ExtractTextPlugin.extract()生成loader用于提取css文件
                use: extractCss.extract({
                    fallback: 'style-loader',
                    use: [{
                            loader: 'css-loader'
                        },
                        {
                          loader:'postcss-loader'
                        }
                    ]
                })
            },
            {
                test: /\.styl$/,
                use: extractStylus.extract({
                    fallback: 'style-loader',
                    use: [{
                            loader: 'css-loader'
                        },
                        {
                         loader:'postcss-loader'
                        },
                        {
                            loader: 'stylus-loader'
                        }
                    ]
                })
            },
    ]
}
...
plugins:[
    extractCss,
    extractStyl
]

html模板

作用:依据一个简单的index.html文件 生成一个自动引用你打包后文件的新index.html 在每次生成到的js文件名称不同时非常有用(比如添加了hash值);

安装:

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

生成单个目标文件:

const HtmlWebpackPlugin = require('html-webpack-plugin')
...
plugins:[
         ...
    new HtmlWebpackPlugin({
        template:''//模板路径
    })
]

生成多个目标文件:

// webapck.config.js
var  HtmlWebpackPlugin = require('html-webpack-plugin')
...
var getHtmlConfig = function (name) {
    return {
        template: './src/view/' + name + '.html',
        filename: 'view/' + name + '.html', // webpackConfig.output中的路径
        inject: true,
        hash: true,
        chunks: ['common', name] // 打包的模块 针对多入口文件 导入模块的关键
    }
...
plugins:[
    new HtmlWebpackPlugin('index'),
    new HtmlWebpackPlugin('login')
]  

####热替换

作用:热模块替换的好处是只替换更新的部分,而不是页面重载。

使用:

1.webpack配置文件中:

new webpack.HotModuleReplacementPlugin()

2.webpack Dev Server中添加 “hot”参数

var config = {
    ...
    devServer:{
        contentBase:'./dist',
        inline:true,
        historyApiFallback:true,
        hot:true
    }
}
module.exports = config

产品阶段的构建

创建一个webpack.production.config.js

修改package.json文件中

...
"scripts":{
    "build":"NODE_ENV=production webpack --config ./webpack.production.config.js --progress"
}

优化插件

  • OccurrenceOrderPlugin为组件分配id,通过这个插件webpack可以分析恶化优先考虑使用最多的模块,并为他们分配最小ID;
  • UglifyJsPlugin 压缩js代码
  • ExtractTextPlugin分离css js文件

缓存

缓存无处不在,使用缓存的最好方法是保证你的文件名和文件内容是匹配的(内容改变,名称也改变)

webpack可以把一个哈希值添加到打包的文件中去,添加特殊的字符串混合体[name],[id],[hash]到输出文件前面。

{
    ...
    output:{
        path:__dirname+"/build",
        fileName:"bundle-[hash].js"
    }
    plugins:[
        new webpack.optimize.OccurrenceOrderPlugin()
        new webpack.optimize.UglifyJsPlugin(),
    ]
    ....
}

去除build文件中残余文件

使用hash后,导致文件越来越多,需要清除残余文件

clean-webpack-plugin

$ cnpm install clean-webpack-plugin --save-dev

使用:

const CleanWebpackPlugin = require('clean-webpack-plugin');
...
plugins:[
    new CleanWebpackPlugin('build/*.*',{
        root:__dirname,
        verbose:true,
        dry:false
    })
]

最佳实践

juqery项目中的实践心得

配置文件中必须有一行:

// 全局的jquery某些插件依赖全局的jquery。
// 加载外部的jquery
externals: {
        jquery: 'window.jQuery'
    }

Vue项目中的实践心得


React项目中的实战心得