webpack打包

webpack打包:

在使用webpack打包react项目,每次需要手动编译比较慢,webpack提供了一种热加载的方式,每次变化时自动编译及刷新页面,提升开发效率。同时,在本地开发时使用webpack服务器做代理,做接口mock数据的转发。
1、首先安装webpack相关包:配置package.json文件,使用npm install 安装需要的包
package.json文件

{
  "name": "cl_html",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "babelify": "^6.3.0",
    "react": "^0.14.8",
    "react-dom": "^0.14.8",
    "react-hot-loader": "^1.3.0",
    "watchify": "^3.4.0"
  },
  "scripts": {
    "start": "webpack-dev-server",
    "build": "webpack",
    "dev": "webpack-dev-server --devtool eval --progress --colors --content-base build"
  },
  "devDependencies": {
    "babel-core": "^6.17.0",
    "babel-loader": "^6.2.5",
    "babel-preset-es2015": "^6.16.0",
    "babel-preset-react": "^6.16.0",
    "browser-sync": "^2.11.1",
    "css-loader": "^0.25.0",
    "file-loader": "^0.9.0",
    "gulp": "^3.9.1",
    "gulp-if": "^2.0.1",
    "gulp-minify-html": "^1.0.6",
    "gulp-processhtml": "^1.1.0",
    "gulp-replace": "^0.5.4",
    "gulp-rev": "^7.0.0",
    "gulp-rev-collector": "^1.0.3",
    "gulp-rev-replace": "^0.4.3",
    "html-webpack-plugin": "^2.30.1",
    "react-hot-loader": "^1.3.0",
    "run-sequence": "^1.2.1",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "webpack": "^1.13.2",
    "webpack-dev-server": "^1.16.2",
    "yargs": "^4.7.1",
     "html-webpack-plugin": "^2.30.1"
  }
}
View Code

2、webpack.config.js配置文件:

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

module.exports = {  // 主要有entry,output、module、plugins等配置项
   //target:"web",   //服务器和浏览器代码都可以用 JavaScript 编写,所以 webpack 提供了多种构建目标(target),你可以在你的 webpack 配置中设置。 默认是web 也可以设置为node

   //1、入口配置
   //单个入口的写法
   //entry: path.resolve(__dirname, 'js/app.js'),   //简单配置写法
   // entry:{
   //    main:path.resolve(__dirname, 'js/app.js'),
   // },
   //向 entry 属性传入「文件路径(file path)数组」将创建“多个主入口(multi-main entry)”
   // entry:  ['webpack/hot/dev-server', path.resolve(__dirname, 'js/app.js')],

   //非单页
   /*entry:{
      index:path.resolve(__dirname, 'js/app.js'),   //pageOne、pageTwo作为打包后的filename
      pageTwo:path.resolve(__dirname, 'js/app2.js')
   },*/
  /* entry:{
      pageOne:path.resolve(__dirname, 'js/app1.js'),
      pageTwo:path.resolve(__dirname, 'js/app2.js'),
      pageThree:path.resolve(__dirname, 'js/app3.js'),
   },*/
  entry: {
    index: [
      'webpack-dev-server/client?http://localhost:8099',
      'webpack/hot/only-dev-server',
      './js/index.js'
    ],
    common:['react','react-dom']
  },
  //2 、定义打包后的文件输出 单个入口的打包输出
  /*output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'index.min.js'
  },*/
  //多入口的打包
   output:{
      path: path.resolve(__dirname, 'build'),
      publicPath: "http://localhost:8099/build/",
      // filename:'[name].min.js',
      filename: '[name].[hash:6].js',
  },

  //使用CDN和hash
  /*  output: {
        path: "/home/proj/cdn/assets/[hash]",
        publicPath: "http://cdn.example.com/assets/[hash]/"
    },*/

  //loader 让 webpack 能够去处理那些非 JavaScript 文件
  module: {
    loaders: [  //在 webpack 的配置中 loader 有两个目标。

// 1、识别出应该被对应的 loader 进行转换的那些文件。(使用 test 属性)
// 2、转换这些文件,从而使其能够被添加到依赖图中(并且最终添加到 bundle 中)(use 属性)
    {
      test: /.jsx?$/,
      exclude: /node_modules/, //include exclude 添加必须处理的文件夹或屏蔽不需处理的文件夹
      loader: 'babel', //loader的名称  babel的配置项放在babelrc文件里面
      query: {
        presets: ['es2015','react']
      }
    },
    {
      test: /.css$/,
      loader: 'style!css'
    },
    {
      test: /.(png|jpg|gif)$/,
      loader: 'url-loader?limit=8192' // 这里的 limit=8192 表示用 base64 编码 <= 8K 的图像
    },
    // {
    //   test: /.js?$/,
    //   loaders: ['react-hot', 'babel'],
    //   include: [path.join(__dirname, 'js')]
    // }
    ]
  },
  //生成source maps 便于调试
    //配置生成Source Maps,选择合适的选项 source-map、cheap-module-source-map、eval-source-map、cheap-module-eval-source-map
    devtool: 'eval-source-map',

//自动刷新配置(1)以命令行方式启动,配置devServer (2)以node 方式启动一个服务
    //命令行启动时 本地服务器  让浏览器检测代码的变化,自动刷新
  /*  devServer: {
        // contentBase: "./public",//本地服务器所加载的页面所在的目录,默认为根目录
        port: 8088,
        colors: true, //终端中输出结果为彩色
        historyApiFallback: true, //不跳转
        inline: true, //实时刷新
        hot: true //自动刷新
    },*/
  plugins: [
    //版权声明插件:在打包后的文件里添加版权声明
    new webpack.BannerPlugin("Copyright PAS Frontend."),

    // 设置生产环境全局变量,以便告诉所有类库,过滤不必要的代码
    new webpack.DefinePlugin({
        'process.env': {
            'NODE_ENV': "production"
        }
    }),

    // 压缩代码,去掉评论注释等  减少文件体积
     new webpack.optimize.UglifyJsPlugin({
         output: {
          comments: false,
        },
        compress: {
          warnings: false
        }
     }),  //压缩
     //每次打包后index.html中的路径也会自动加上hash值
     new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './index.html'),
      filename:path.resolve(__dirname, './index.build.html'),  //js路径替换成带hash的
       hash:true,
       title:"标题"
    }),

        // 拆分插件: 打包成不同文件
        new webpack.optimize.CommonsChunkPlugin({
            name: 'common', // 上面入口定义的节点组
            filename: 'build-common.js' //最后生成的文件名
        }),


        //热加载插件HMR
        // new webpack.HotModuleReplacementPlugin(),
      new webpack.HotModuleReplacementPlugin(),
      // new webpack.NoErrorsPlugin()
  ]
}
View Code

(1)entry配置项:
//单个入口的写法
entry: path.resolve(__dirname, 'js/app.js'), //简单配置写法
//对象形式传入,配置多个入口文件

entry:{
index:path.resolve(__dirname, 'js/app.js'), //index、pageTwo作为output打包后的filename
pageTwo:path.resolve(__dirname, 'js/app2.js')
}
//向 entry 属性传入「文件路径(file path)数组」将创建“多个主入口(multi-main entry)”
// entry: ['webpack/hot/dev-server', path.resolve(__dirname, 'js/app.js')],


(2)多入口的打包的输出配置,如果需要,filename可以配置参数,加上hash。
publicPath来指定编译后的包(bundle)的访问位置。另外,打包后的文件加上随机字符串后,在index.html中引入的脚本路径也需要动态调整,这个publicPath会作为脚本的根路径

output:{
publicPath: "http://localhost:8099/build/",
path: path.resolve(__dirname, 'build'),
filename: '[name].[hash:6].js', //name就是entry中配置的键名,hash用于指定生成随机6为随机字符
}


(3)module 主要处理一些es6编译、css预处理、图片处理等
//使用loaders对模块进行各种处理,如将es7es6转成es5

module: {
loaders: [{
test: /.jsx?$/, //匹配要处理文件的扩展名 (必选)
exclude: /node_modules/, //include exclude 添加必须处理的文件夹或屏蔽不需处理的文件夹
loader: 'babel', //loader的名称 babel的配置项放在babelrc文件里面
// query: { //为loader提供额外的设置选项。 //babel配置选项可以写在.babelrc文件中
// presets: ['es2015','react'] //es2015 解析es6、 react 解析react中的jsx
// }
},
{
test: /.css$/,
// loader: 'style!css' //style-loader css-loader
/* loader: 'style!css?modules' //css module*/
loader: 'style!css?modules!postcss'
},
{
test: /.(png|jpg|gif)$/,
loader: 'url-loader?limit=8192' // 这里的 limit=8192 表示用 base64 编码 <= 8K 的图像
},
//json文件处理
{
test: /.json$/,
loader: "json"
}
]
},

(4)plugins:webpack插件,压缩、热加载、拆分打包等,有些插件是webpack自带的可以直接使用,如BannerPlugin、UglifyJsPlugin等,但有些如HtmlWebpackPlugin需要自己安装 (npm install html-webpack-plugin --save-dev)

plugins: [
//版权声明插件:在打包后的文件里添加版权声明
new webpack.BannerPlugin("Copyright PAS Frontend."),

// 设置生产环境全局变量,以便告诉所有类库,过滤不必要的代码
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': "production"
}
}),

// 压缩代码,去掉评论注释等 减少文件体积
new webpack.optimize.UglifyJsPlugin({
output: {
comments: false,
},
compress: {
warnings: false
}
}), //压缩
//每次打包后index.html中的路径也会自动加上hash值
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './index.html'),
filename:path.resolve(__dirname, './index.build.html'), //js路径替换成带hash的
hash:true,
title:"标题"
}),

// 拆分插件: 打包成不同文件
new webpack.optimize.CommonsChunkPlugin({
name: 'common', // 上面入口定义的节点组
filename: 'build-common.js' //最后生成的文件名
}),


//热加载插件HMR
// new webpack.HotModuleReplacementPlugin(),
new webpack.HotModuleReplacementPlugin(),
// new webpack.NoErrorsPlugin()
]


3、热加载 webpack-dev-server
webpack-dev-server是一个小型的Node.js Express服务器,它使用webpack-dev-middleware来服务于webpack的包,除此自外,它还有一个通过Sock.js来连接到服务器的微型运行时.
安装使用 npm install webpack-dev-server

(1)启动一个webpack-server服务,使用该服务访问项目文件,如http://localhost:8099/index.html,后续再修改脚本时,不需要手动执行webpack打包,webpack-server会自动监听变化并刷新页面。

webpack.server.js文件,使用node webpack.server.js启动服务
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');

new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: false,
proxy: { //接口转发
"/restapi/hkstock": "http://localhost:3009/"
},
watchOptions: {
aggregateTimeout: 300,
poll: 1000
},
}).listen(8099, 'localhost', function (err, result) {
if (err) console.log(err);
console.log('Listening at localhost:8099');
});

(2)webpack.config.js配置,配置entry、output,同时由于加入hash,index.html中的路径也需要动态改变。因此引入 HtmlWebpackPlugin插件,动态生成一个index.html,修改里面的js路径。

entry: {
index: [
'webpack-dev-server/client?http://localhost:8099', //监听该服务下的变化
'webpack/hot/only-dev-server',
'./js/index.js'
]
},
output:{
path: path.resolve(__dirname, 'build'),
publicPath: "http://localhost:8099/build/",
// filename:'[name].min.js',
filename: '[name].[hash:6].js',
},
plugins:[
//每次打包后index.html中的路径也会自动加上hash值
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './index.html'),
filename:path.resolve(__dirname, './index.build.html'), //js路径替换成带hash的
hash:true,
title:"标题"
}),
//热加载插件HMR
new webpack.HotModuleReplacementPlugin(),
]


4、提取公共模块
参考:https://segmentfault.com/a/1190000011920743

如果文件是多入口的文件,可能存在,重复代码,把公共代码提取出来,又不会重复下载公共代码了(多个页面间会共享此文件的缓存)。
对于SPA 应用来说没有特别的需要分离出模块,但是针对首屏渲染速度的提升,可以将某些独立模块分离出来实现按需加载。
// CommonsChunkPlugin的初始化常用参数有解析?
// name: 这个给公共代码的chunk唯一的标识
// filename,如何命名打包后生产的js文件,也是可以用上[name]、[hash]、[chunkhash]
// minChunks,公共代码的判断标准:某个js模块被多少个chunk加载了才算是公共代码

new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename:'vendor.min.js',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),

5、代码分割
单页面应用的公共模块没有必要提取出单独的文件,因为不必考虑复用的情况。但是对于打包生成的文件过大,我们又想分离出几个模块有需要的时候才加载,其实这并不是提取公共模块,而是代码分割,通过:
new webpack.optimize.CommonsChunkPlugin({
name: ['生成的项目公共模块文件名', '第三方模块文件名'],
minChunks: 2,
}),

参考:

https://segmentfault.com/a/1190000006964335

https://segmentfault.com/a/1190000011920743

原文地址:https://www.cnblogs.com/lydialee/p/8359374.html