webpack简单使用与优化

1、使用

关于webpack的使用,在这借鉴官方文档,作一些简单总结。css

webpack4之后会有默认的配置体系,简单来讲项目根目录下默认会有个webpack.config.js的配置文件,其中入口文件为src/index.js,打包出口文件夹为dist,通常来讲咱们平时项目也是按这个规则来的。html

会有首先看一下一个简单的webpack基本配置结构node

{
    entry:'./src/index.js',
    output:{
        path: __dirname+'/dist',
        filename: '[name].js'
    },
    mode:'production',
    module:{},
    plugins:[]
}
复制代码

1.1 入口

entry,指定入口文件,地址是以项目为根节点的相对路径的字符(以单页面为例,只有一个入口)react

1.2 输出

output,指定输出,参数是一个对象,其中path指定输出的文件夹,地址须要使用绝对路径webpack

1.3 模式

mode,通常是两个值'developement'和'production',做用用来标记对应开发和生产环境,同时webpack会根据值不一样进行一些默认配置,好比production会压缩代码;同时,在开发或者打包过程当中,会在Node的模块process中把值存进变量process.env.NODE_ENV中供使用。web

1.4 模块

module,由于webpack是基于Node,自己能运行识别js文件,因此其它模块,如css、图片等经过配置module中的loader来加载这些文件,同时借助loader也能够编译处理ES六、TS等上层语法、模板。json

1.5 插件

plugins,如文档所说,插件是webpack的支柱功能,目的在于解决loader没法实现的事。好比常见的UglifyJsPlugin压缩代码、CopyWebpackPlugin复制静态资源、HtmlWebpackPlugin使用html模板等等浏览器

2、优化

前面介绍的webpack的简单使用,官方文档很详细就一带而过了。接下来总结下webpack优化相关的东西,优化这部分,我把它分为三个部分:代码风格、构建效率、加载性能缓存

2.1 代码风格

开发单页面时,都会配置一套开发环境和一套打包操做,而后你有一个朋友就是我系列通常会有三个配置文件;服务器

  • webpack.base.js:基础配置
  • webpack.dev.js:开发环境配置
  • webpack.pro.js:打包配置

webpack.base.js公共配置部分,而后生产和打包配置经过merge的方式扩展。

package.json的scripts中配置命令行;

  • "dev":"webpack --config webpack.dev.js",
  • "build":"webpack --config webpack.pro.js"

这样的确也很清晰,也没问题。可是后来我使用React脚手架create-react-app,看了一下配置文件以为更加合理。

首先它并无在命令行中使用webpack,这样就省去安装webpack-cli

create-react-app中的package.json的scripts中配置命令行;

  • "start": "node scripts/start.js",
  • "build": "node scripts/build.js"

项目中写好脚本直接使用node执行,配置文件比较多就模拟主要内容

// webpack.config.js
module.exports = function(webpackEnv){
    const isEnvDevelopment = webpackEnv === 'development';
    const isEnvProduction = webpackEnv === 'production';
    return {
        // ...
        mode: webpackEnv,
        // ...
        plugins:[
            new pulicPlugin(),
            isEnvDevelopment && new Plugin1(),
            isEnvProduction && new Plugin2(),
        ].filter(Boolean)
        // ....
    }
}
复制代码

webpack.config.js导出一个函数,指定环境做为参数,而后经过参数进行定制,返回一个webpack的配置参数,start.jsbuild.js主体内容也大概能够猜测到了

// start.js
const configFactory = require('webpack.config');
const webpack = require('webpack');
const config = configFactory('development');
const complier = webpack(config);
return new Promise((resolve, reject) => {
    compiler.run((err, stats) => {
      let messages;
      if (err) {
        if (!err.message) {
          return reject(err);
        }
        // ....
      }
    // .....
    })
复制代码

首先经过传参方式能够省去安装webpack-merge,并且原来使用的方式,只执行到webpack编译,而脚手架还补充了编译后的统计分析。

2.2 构建效率

  • 一、dllPlugin

dll意思是动态连接库,在webpack的主要思想就是将一些第三方库打包分离出来,只要第三方库不须要升级,每次项目启动或者打包时就不须要重复打包这些库了。

具体用法以react项目为例,在config新增一个webpack.dll.config.js配置

const webpack = require('webpack');
const path = require('path');
module.exports =  {
    entry: {
        vendor:[
            'react',
            'react-dom',
            'react-router-dom'
        ]
    },
    output: {
        path: path.resolve(__dirname,'../dist'),
        filename: '[name].js',
        library: '[name]_[hash]',
    },
    plugins: [
        new webpack.DllPlugin({
            context: __dirname,
            name: '[name]_[hash]', //须要与output.library相同
            path: path.resolve(__dirname, '../dist/[name]-manifest.json'),
        })
    ]
}
复制代码

package.json增长命令行"build:dll": "webpack --config config/webpack.dll.config.js --mode production"

执行命令后会在dist文件夹下生成vendor-manifest.jsonvendor.js两个文件,vendor.js也就是分离出的静态资源,而vendor-manifest.json一个清单文件来描述资源的来源路径。

分离完成后就是经过DllReferencePlugin供项目使用了,在webpack.config.js中添加配置

new webpack.DllReferencePlugin({
  context: __dirname,
  manifest: require('../dist/vendor-manifest.json'), //指定dll描述的位置
}),
复制代码

这样经过dll分离就完成了,这时候还差一步,咱们运行项目发现html并无引入vendor.js,这里须要手动更新html模板,或者使用插件add-asset-html-webpack-plugin添加。

const AddAsssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
//....
plugins:[
  // ...
  new AddAsssetHtmlWebpackPlugin({
    filepath: path.resolve(__dirname, '../dist/*.js'),
    hash:true, //附加hash到文件名后,防止缓存
    outputPath: 'static/js', //
    publicPath: '/static/js'
  })
  // ....
]
复制代码

这样完成后的项目就能够正常运行了。

  • 二、HappyPack

happypack提高webpack打包速度,本质上happypack是用经过js的多进程来实现打包加速。

const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
// 引入happypack,ThreadPool线程池使用系统核心数
module.exports = {
  // ....
  module: {
    rules: [
      {
        test:/\.js$/,
        use:[
          {
            // loader:'babel-loader',
            // options:{
            // ....
            // }
            loader:'happypack/loader?id=happyBabel', //在loader中添加触发happypack,将原来的配置转移给happypack
          }
        ]
      }
    ]
  },
  plugins:[
    new HappyPack({
      id:'happyBabel', //id与loader中配置的id参数相同
      threadPool: happyThreadPool,
      verbose: false, //是否输出过程日志
      loaders:[
        {
          loader:'babel-loader', 
          options:{
            ....
          }
        }
      ]
    })
  ]
}
复制代码

2.3 加载性能

  • 一、Gzip

HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点经常使用GZIP压缩技术来让用户感觉更快的速度。这通常是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.通常对纯文本内容可压缩到原大小的40%.这样传输就快了,效果就是你点击网址后会很快的显示出来.固然这也会增长服务器的负载. 通常服务器中都安装有这个功能模块的。

以上是引用的一部分介绍,如今主流的浏览器都支持Gzip,简单来讲Gzip在本来打包压缩后的基础上再压缩一次,Gzip的主要原理就是将重复的字符片断临时替换来减少文件大小。

如何查看?

浏览器客户端的http请求头中的有一个Accept-Encoding字段,告诉服务器接受的编码格式,它的值中包含gzip就说明浏览器支持解析Gzip的返回;若是服务器开启且返回了Gzip的响应,这时响应头会有个的content-encoding字段,内容为content-encoding:gzip。也就是响应在服务端压缩成Gzip,而后客户端再解压使用。

如何使用?

create-react-app脚手架为例,打包完成后控制台输出如下一些内容。

build

文件大小提示是使用Gzip压缩后的大小,咱们审查真实文件大小确实是要大很多,并且Gzip文件后缀名是gz。对于静态资源的Gzip,这里须要在webpack中增长compression-webpack-plugin的配置。

const CompressWebpackPlugin = require('compression-webpack-plugin');
// 添加到plugins
new CompressWebpackPlugin({
    test: /\.(js|css)$/, // 压缩js和css
    threshold: 10240, // 达到10k才启用,资源太小不必,并且解压也是有损耗
    minRatio: 0.6 //压缩最小比例下限
    // 其余参数使用默认
  })
复制代码

完成配置后,打包后的代码静态资源知足以上条件的会多出一个之后缀为.gz的gzip文件,客户端配置也就完成了。当支持解析Gzip的客户端请求服务端的静态资源就会自动请求.gz的文件。压缩效率,以antd为例,完整的普通压缩包体积会接近2M,但Gzip压缩后体积会缩小为不到600KB的大小,结合Gzip的原理,这种复用性强的UI组件压缩效率确定会比较高。

静态资源搞定了,服务端也能够作一些优化。服务端使用Gzip主要是对一些接口返回的数据进行压缩,以Koa为例,安装koa-compress

const compress = require('koa-compress')
const Koa = require('koa')
const app = new Koa()
app.use(compress({
  filter: function (content_type) {
  	return /text/i.test(content_type)
  },
  threshold: 2048,
  flush: require('zlib').Z_SYNC_FLUSH
}))
复制代码

使用demo的默认配置,完成后在接口响应值在知足配置条件下就会被压缩成Gzip返回给客户端了。

  • 二、按需加载

单页面应用中的多路由,为防止第一次加载页面加载过多资源,将路由配置成按需加载。

一篇讲述代码分割的文章

react项目为例,能够借助react-loadable,配置路由组件

import Loadable from 'react-loadable';

// 修改前配置,打包之后进入首页就会加载相应资源
import oldViewComponent from './viewComponent';
<Route path='/view' component={oldViewComponent} />

// 修改后,路由跳转/view才会加载
const newViewComponent = Loadable({
    loader: () => import('./viewComponent'),
    loading: Loading //加载完成前显示,通常用一个loading组件
})
<Route path='/view' component={newViewComponent} />
复制代码
相关文章
相关标签/搜索