Webpack基础使用方法

224 阅读5分钟

什么是webpack

webpack是一种javascript应用程序的静态模块打包器(module bundler)。

什么是babel

Babel 是一个 JavaScript 编译器 Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。webpack2后自带babel-loader。

webpack四个核心概念

  • 入口(entry)

    入口指webpack要打包(构建内部依赖)的起始文件,默认是'./src'

    module.exports={
    	entry:'./src/xxxx.js'
    }
    
  • 输出(output)

    output是用来告诉webpack在哪里输出它创建的包(bundles),以及打包后文件的名字。

    const path=require('path')
    module.exports={
    	entry:'./src/xxxx.js',
    	output:{
        	path:path.resolve(__dirname,dist),
            filename:'suibiannong.xxxx.js'
        }
    }
    
  • loader

    loader,字面意思,加载器,可以把所有非javascript类型的文件在打包成应用程序所依赖的包前按loader要求转译(因为webpack默认只转译javascript),loader有两个重要属性;

    test:用于识别需要转译的特定文件或某些文件

    use:具体使用的loader名

使用某个loader,只需要npm install xxx-loader -D或者yarn add xxx-loader -D

module.exports= {
  /*...*/
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' },
      {test:/\.css/,use:[{loader:'css-loader'},{loader:'style-loader']}}
    ]
  }
};
  • 插件(plugins)

    loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。

由于插件可传递参数,选项,所以在引入插件后,需要new插件,

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

webpack其他配置和常用操作

  • 模式(mode)

webpack4开始,就加入了mode选项,mode告知 webpack 使用相应模式的内置优化(也就是使用不同的内置插件)。

module.exports = {
  mode: 'production' //or 'development'
};

在wp4之前,可通过插件配置process.env.NODE_ENV

  • devServer

使用webpack开发,一般还会安装webpack-dev-server,它会创建一个简单的web服务器,用于开发模式时,实时重新加载模块。

示例:yarn add webpack-dev-server --save-dev

module.exports={
	/*...*/
    devServer:{
    	contentBase:'./dist'   //web服务器建立后,设置dist为可访问文件
    }
}
  • HMR

webpack热模块(Hot Module Replacement),无需刷新,实时更新各个模块。但它只适用于开发坏境,不适用于生产环境。配置也很简单,只需在的devServer基础上进行配置。

const webpack=require('webpack')  //引入内置webpack

module.exports={
	/*...*/
    devServer:{
    	contentBase:'./dist',
        hot:true  //开启热模块
    },
    plugins:[
    	new webpack.HotModuleReplacementPlugin()  //引入内置热模块插件
    ]
}
  • Source Map

devtool是webpack用来设置资源映射的选项,便于debug及维护,在生产环境中,要生成小的bundle,同时速度要快,就可关闭资源映射,或者酌情使用devtool:'source-map',使用了UglifyJSPlugin插件,要设置插件选项{sourceMap:true}

  • 懒加载

懒加载或者按需加载,是一种很好的优化网页或应用的方式。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

/*index.js*/

import('./src/xx.js').then(module=>{
	let xx=module.default  //xx.js必须使用的是export default导出
    xx()
})

在这里import(路径)返回的是一个Promise,可用Promise.then().catch()等链式操作。也可以用async,await调用。

  • tree shaking

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。 在webpack中,删除未导入代码必须:

  1. 使用 ES2015 模块语法(即 import 和 export)。

    按需导入使用到的模块。

  2. 在项目 package.json 文件中,添加一个 "sideEffects" 入口。

    设置有副作用的文件(也就是不要应用tree shaking的模块),避免误删。

  3. 引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。

    配置要使用的压缩代码工具

  • shimming

一些第三方的库(library)可能会引用一些全局依赖(例如 lodash 中的 _)。这些库也可能创建一些需要被导出的全局变量。providePlugin的作用就是如果 webpack 知道这个变量在某个模块中被使用了,那么 webpack 将在最终 bundle 中引入我们给定的 package。(简言之,如果模块使用到了这个全局变量,就生成一个单独的bundle文件,供调用)

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

module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist')
   }
   },
   plugins: [
     new webpack.ProvidePlugin({
       _: 'lodash' 
     })
   ]
};

这样我就可以在使用到lodash的地方不用import _ from 'lodash'

同时,如果配合tree shaking,我们还可以只导入包的部分值,例如,这个项目只用到了_.join(),那么可以这样配置:

module.exports={
	/*...*/
	plugins: [
     new webpack.providePlugin({
-       _: 'lodash'
+       join:['lodash','join']   //意思是用到join()的地方就是lodash.join()
     })
   ]
}
  • 代码分离

代码分离其实是一种思想,要不同模块的代码生成不同的文件,或者提取共有代码为共有文件, 在webpack中,如果有两个入口文件,最后生成两个outputFile,那么避免两个文件生成有重复bundle,我们可以引入webpack的CommonsChunkPlugin

 const path = require('path');
 const webpack = require('webpack');
 const HTMLWebpackPlugin = require('html-webpack-plugin');

 module.exports = {
   entry: {
     index: './src/index.js',
     another: './src/another-module.js'
   },
   plugins: [
     new HTMLWebpackPlugin({
       title: 'Code Splitting'
     }),
     new webpack.optimize.CommonsChunkPlugin({
       name: 'common' // 指定公共 bundle 的名称。
     })
   ],
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist')
   }
 };
  • 缓存

缓存是浏览器为节约加载时间从对文件进行缓存,如果文件名变了,那么就会重新读取某个文件,例如lodash是一个单独的bundle,那么修改其他文件后的webpack处理是不会对loadsh重新bundle的,也就是不会修改他的chunkhash。

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

  module.exports = {
    entry: {
      main: './src/index.js',
      vendor: [
        'lodash'
      ]
    },
    plugins: [
      new webpack.HashedModuleIdsPlugin(),    //对没有修改的文件保持chunkhash值不变
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor'      //将lodash提取为单独chunk文件
      }),
      new webpack.optimize.CommonsChunkPlugin({
        name: 'manifest'    //将manifest提取为单独chunk文件
      })
    ],
    output: {
      filename: '[name].[chunkhash].js',   //生成的chunk都有一串hash随机数
      path: path.resolve(__dirname, 'dist')
    }
  };

如上配置,每次lodash的chunk文件hash值都不会改变,浏览器也就不会重复读取该文件。

  • 生产环境构建

如上所述,有的配置适用于开发及环境,有的配置适用于生产环境,那么webpack配置文件就需要设置为webpack.common.js;webpack.prod.js;webpack.dev.js,

webpack.common.js用来设置基础配置;

webpack.prod.js用来配置生产环境属性;

webpack.dev.js用来配置开发环境属性;

使用webpack-merge包进行webpack文件合并,安装yarn add webpack-merge --save-dev

/*webpack.dev.js*/
const common=require('./webpack.common.js');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports=merge(common,{
	plugins: [
      new UglifyJSPlugin()
    ]
})

webpack.dev.js同理进行配置合并。

在package.json中添加脚本;

/*package.json*/
{
	scripts:{
    	"start":"webpack-dev-server --open --config webpack.dev.js",
        "build":"webpack --config webpack.prod.js"
    }
}

总结

以上是webpack的简单实用总结,更多细节需要查询webpack官方文档