使用babel和webpack优化项目

422 阅读5分钟

前言

组件化、模块化、自动化、工程化是现在考核一前端项目是否为一个优秀项目的标准之一,从而应运而生了一些列前端模块化、工程化的工具库。babel和webpack就是其中的优秀代表,那你了解他们么?你知道如何通过babel和webpack来优化前端项目么?

正文

webpack

webpack是一款前端构建工具,他的主要作用就是将各个具有依赖的模块打包整合在一起生成静态资源。就像官网展示的图片:

module chunk bundle的区别

  • module: 各个源码文件,webpack中一切皆模块
  • chunk: 多个模块拼接合成的文件,使用entry,import(),splitChunk可以生成chunk
  • bundle: 最终输出的文件称为bundle

loader和plugin的区别

  • loader: 模块转换器。less=>css
  • plugin: 扩展插件

webpack性能优化

基本分为两个方面:

  • 优化打包构建速度
  • 优化产出代码

优化打包构建速度

优化babel-loader

  • 启用缓存use:['babel-loader?cacheDirectory]
  • 合理使用include和exclude

IgnorePlugin: 忽略本地化内容

new Webpack.IgnorePlugin(/\.\/locale/,/moment/),//moment这个库中,如果引用了./locale/目录的内容,就忽略掉,不会打包进去

// index.js 在文件中需要手动引入当前包内自己所需要的部分
import moment from 'moment'
//手动引入所需要的语言包
import 'moment/locale/zh-cn';
moment.locale('zh-cn');

noParse: 打包时不去解析相关模块中是否还依赖其他包,而这些三方库里面没有其他依赖

module:{
  noParse:/jquery|lodash/,//不去解析jquery、lodash中的依赖库
}

happyPack: 开启多进程打包,提高构建速度。

  // loader
{
  text:/\.js$/,
  use:['happypack/loader?id=babel'],
}
// plugins
new HappyPack({
    id:'babel',
    loaders:[{
      loader: 'babel-loader',
      options:{
        cacheDirectory: true,
      }
    }]
})

parallelUglifyPlugin

  • webpack内置了一款Uglify工具压缩js,但是不能开启多进程
  • 项目比较大。构建速度缓慢才需要使用多进程,项目简单开启多进程开销太大,可能拖慢打包进程
new ParallelUglifyPlugin({
  uglifyJS:{
    output: {
      beautify: false,  // 紧凑输出
      comments:false, // 删除注释
    },
    compress:{
      drop_console: true,
      collapse_vars: true,  // 内嵌定义了但是只用到一次的变量
      reduce_vars: true, // 提取出出现多次但是没有定义成变量去引用的静态值
    }
  }
}),

自动刷新: watch: true

热更新: HotModuleReplacementPlugin

对于js文件需要手动配置实现热更新。 css更改自动实现热更新。

DllPlugin: 动态链接库

  • 首先使用webpack.DllPlugin打包出dll.js文件和mainfest.json
entry:{
  vendors:['lodash'],
  react:['react','react-dom'],
},
output:{
  filename:'[name].dll.js',
  path: path.resolve(__dirname,'../dll'),
  library: '[name]',
},
plugins: [
  new webpack.DllPlugin({
    name: '[name]',
    path: path.resolve(__dirname,'../dll/[name].manifest.json')
  })
]
  • 接着在npm run dev 时使用webpack.DllReferencePlugin关联文件、使用AddAssetHtmlWebpackPlugin在html添加对dll.js文件的引用
const files = fs.readdirSync(path.resolve(__dirname,'../dll'))
files.forEach(file => {
  if(/.*\.dll.js/.test(file)){
    plugins.push(
      new AddAssetHtmlWebpackPlugin({
        filepath: path.resolve(__dirname, '../dll', file)
      }),
    )
  }
  if(/.*\.manifest.json/.test(file)){
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, '../dll', file)
    })
  }
})
  • 不推荐使用在生产环境中

优化产出代码

代码体积更小

  • 开启代码压缩
module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        exclude: /\/node_modules/,
  	  parallel: true, // 开启多线程压缩	
      }),
    ],
  },
};
  • url-loader 产出base64格式的图片
  • Tree-Shaking: mode: 'production'
    • 只有使用ES Module引用才能实现Tree-Shaking
  • Scope Hosting:代码体积更小,创建函数作用域更少,代码可读性更高
    // 针对npm中第三方模块优先采用jsnext:main中指向的ES6 模块化语法的文件
    resolve: ['jsnext:main', 'browser', 'main'],
    
    plugin:{
      // 开启Scope Hosting
      new webpack.optimize.ModuleConcatenationPlugin()
    }
    

合理分包,不重复加载

  • 打包出的bundle加Hash,未改变的文件使其能命中缓存
  • 提取公共代码:
optimizatoin:{
  splitChunks:{
    chunks: 'all',
    minSize: 20000, // 生成块的最小大小(以字节为单位)。
    maxSize: 0,
    minChunks: 1,       // 代码被引入了多少次才会代码分隔
    maxAsyncRequests: 6,      // 按需加载时 最多同时 请求数
    maxInitialRequests: 4,      // 入口点的最大并行请求数。
    automaticNameDelimiter: '~',
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        priority: -10,	// 优先级 
      },
      default: {
        minChunks: 2,
        priority: -20,
        reuseExistingChunk: true // 如果当前块包含已从主捆绑包中拆分出的模块,则将重用它,而不是生成新的模块。这可能会影响块的结果文件名。
      }
    }
  },
}

速度更快,内存使用少

  • 使用懒加载import()
  • 使用CDN加速: publicPath:'http://cnd.com'

babel

babel是一款JavaScript的编译器。他负责

  • 检查语法是否符合es5语法规范。不符合就转换
  • 不处理模块化(webpack处理)

presets

presets是babel中的预设组件。相当于一堆plugin的集合。 常见的有

  • @babel/preset-env
  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript

babel-polyfill

  • core-js: 将ES6、ES7等代码转换整ES5代码的工具库
  • regenerator: 将generator函数转换为ES5代码的工具库

babel-polyfill就是两者的集合。babel在7.4版本放弃了babel-polyfill推荐用户直接使用core-jsregenerator

如何实现按需引用,在.babelrc文件下

{
  "presets":[
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage", // 按需引用
        "corejs": 3, //core-js 版本
        "regenerator": true,
      }
    ]
  ]
}

babel-runtime

babel-polyfill会造成全局污染,runtime就是为了防止全局污染。写第三方库时可以使用。

.babelrc

{
  "plugins":[
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        "corejs": false,
        "helpers": true,
        "regenerator": true,
        "useESModules": false,
      }
    ]
  ]
}

前端为何要进行打包和构建?

  • 体积更小(Tree-Shaking、压缩、合并),加载更快
  • 编译高级语言和语法(TS、ES6+、模块化、sass、less)
  • 兼容性和语法检查(polyfill、postcss、eslint)
  • 统一高校的开发环境
  • 统一的构建流程和产出标准
  • 集成公司构建规范(提测、上线)

关注公众号

喜欢的同学可以关注公众号