webpack

什么是webpack

webpack是一个模块打包器,包(bundle)就是一个js文件,它把一堆资源合并在一起,以便在同一个文件请求中发回给客户端,webpack还能处理一些浏览器不能直接运行的拓展语言,如Scss,typeScript等。webpack是基于配置型的构建工具,它把整个项目作为一个整体,通过一个给定的主文件(如index.js),从这个主文件开始找到项目所有的依赖文件,使用loaders处理它们,最终打包为一个或多个浏览器可识别的js文件。

 

webpack和gulp的区别

webpack:作为一个智能解析器,几乎可以处理任何一个第三方的库,无论是commonjs,amd还是普通的js文件,甚至加载依赖的时候可以动态表达式。webpack是一种预编译的解决方案,不需要在浏览器中加载解释器,而且不管是AMD/CMD/ES6风格的模块化,它都认识,并且能编译成浏览器认识的JS。webpack是一种模块化的解决方案,webpack在很多场景下可以替代gulp/grunt类的工具 
gulp:在一个配置文件中,指明对某些文件进行类似编译、组合、压缩等任务的具体步骤,工具之后可以在自动替你完成这些任务。gulp是一个能够优化前端的开发流程的工具。

 

使用webpack

项目环境

//全局安装
npm install -g webpack
//安装到项目里
npm install --save-dev webpack

如果项目中没有package.json这个文件,可以通过以下指令创建:

npm init

一个简单的配置文件

webpack.config.js

// 一个常见的`webpack`配置文件
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
        entry: __dirname + "/app/main.js", //已多次提及的唯一入口文件
        output: {
            path: __dirname + "/build",  //打包后的文件存放的地方
            filename: "bundle-[hash].js"  //打包后输出文件的文件名
        },
        devtool: 'none',
        devServer: {
            contentBase: "./public", //本地服务器所加载的页面所在的目录
            historyApiFallback: true, //不跳转
            inline: true,
            hot: true
        },
        module: {
            rules: [{
                    test: /(\.jsx|\.js)$/,
                    use: {
                        loader: "babel-loader"
                    },
                    exclude: /node_modules/
                }, {
                    test: /\.css$/,
                    use: ExtractTextPlugin.extract({
                        fallback: "style-loader",
                        use: [{
                            loader: "css-loader",
                            options: {
                                modules: true,
                                localIdentName: '[name]__[local]--[hash:base64:5]'
                            }
                        }, {
                            loader: "postcss-loader"
                        }],
                    })
                }
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html" //new 一个这个插件的实例,并传入相关的参数
        }),
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("style.css")
    ]
};

 

配置分析

一 entry和output 
entry用来定义入口文件,可以是字符串或数组或对象 
1.字符串:

module.exports = {
  entry: './main.js'
};

2.数组:除了main.js作为入口js之外,还有一个是用来配置webpack提供的一个静态资源服务器webpack-dev-server,它会监控项目中每个文件的变化,实时构建,并自动刷新页面。

module.exports = {
  entry: [
    'webpack/hot/only-dev-server',
    './main.js'
  ]
};

3.对象:将不同的文件按需构建,按需使用

module.exports = {
  entry: {
    a: './a.js',
    b: './b.js'
  }
};

 

output用于定义构建后的文件的输出,是个对象,其中包含了path和filename

module.exports = {
  output: {
    path: './build',
    filename: 'bundle.js'
  }
};

 

二 devtool 

devtool属性可以配置调试代码的方式,有多种调试方式,一般只在开发时使用,生产环境下应该将值设置为false。常用值有两个:

eval: 可以设断点调试,不显示信息,每个js模块代码用eval()执行,并且在生成的每个模块代码尾部加上注释,不 会生成.map文件。

source-map: 可以设断点调试,不显示列信息,生成相应的.Map文件,并在合并后的代码尾部加上注释//# sourceMappingURL=**.js.map,可以看到模块代码并没有被eval()包裹,此种模式并没有将调试信息放入打 包后的代码中,保持了打包后代码的简洁性.

devtool选项 配置结果
source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包速度;
cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便;
eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项;
cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点;

上述选项由上到下打包速度越来越快,不过同时也具有越来越多的负面作用,较快的打包速度的后果就是对打包后的文件的的执行有一定影响。cheap-module-eval-source-map推荐在大型项目考虑时间成本时使用,对小到中型的项目中,eval-source-map是一个很好的选项。

 

三 devserver 

如果想让你的浏览器监听你的代码修改,并自动刷新显示修改后的结果,webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过这是一个单独的组件,在webpack进行配置之前需要单独安装它作为项目依赖。

npm install --save-dev webpack-dev-server

devserver有一些配置选项,可以参考如下的配置:

devserver的配置选项 功能描述
contentBase 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录
port 设置默认监听端口,如果省略,默认为”8080“
inline 设置为true,当源文件改变时会自动刷新页面
historyApiFallback 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
module.exports = {
  devtool: 'eval-source-map',
  entry:  __dirname + "/app/main.js",
  output: {
    path: __dirname + "/public",
    filename: "bundle.js"
  },
  devServer: {
    contentBase: "./public",//本地服务器所加载的页面所在的目录
    historyApiFallback: true,//不跳转
    inline: true//实时刷新
  } 
}

 

Babel 

Babel是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的: 

  • 让你能使用最新的JavaScript代码(ES6,ES7),而不用管新标准是否被当前使用的浏览器完全支持;
  • 让你能使用基于JavaScript进行拓展的语言,如React的JSX;

Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/public",//打包后的文件存放的地方
        filename: "bundle.js"//打包后输出文件的文件名
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
    },
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            }
        ]
    }
};

现在你的webpack的配置已经允许你使用ES6以及JSX的语法了。

 

五 module

module主要是用来配置加载器(loaders)。webpack本身只能处理javascript模块,如果要处理其他类型的文件,就需要使用loader进行转换,每一种文件会用不同的加载器处理。 
loadres,preLoadres,postLoaders的配置主要包括以下几项:

    • test:一个匹配loaders所处理的文件的拓展名的正则表达式(必须)
    • loader: loader的名称(必须)
    • include/exclude: 手动添加必须处理的文件/文件夹,或屏蔽不需要处理的文件/文件夹(可选)
    • query: 为loaders提供额外的设置选项(可选)
module: {
    exprContextCritical: false,
    rules: [
      {
        test: /\.ts$/,
        enforce: 'pre',
        loader: 'tslint-loader',
        exclude: /target/
      },
      {
        test: /\.tsx?$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: './.babelcache/',
              plugins: ['angularjs-annotate']
            }
          }
        ],
        enforce: 'post',
        exclude: createBabelExclude()
      },
      {
        test: /\.json$/,
        loader: 'json-loader'
      },
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        test: require.resolve('highcharts'),
        loader: 'expose-loader?Highcharts'
      }
    ]
  }

 

六  resolve 

resolve用来配置文件路径的指向。可以定义文件跟模块的默认路径及后缀等,节省webpack搜索文件的时间、优化引用模块时的体验。常用的包括alias,extensions,root,moduleDirectories属性。 

  • alias: 是个对象,把资源路径重定向到另一个路径
  • extensions: 是个数组,定义资源的默认后缀,比如定义后引用a.js、b.json、c.css等资源可以不用写后缀名直接写a、b、c
  • root: 是个数组,通过绝对路径的方式来定义查找模块的文件夹。可以是一个数组,主要是用来增加模块的搜寻位置使用的。root 的值必须是绝对路径,使用path.resolve设置。
  • modulesDirectories: 是个数组,用来设置搜索的目录名的,默认为["web_modules", "node_modules"]

resolve: {
        //查找module的话从这里开始查找
        root: 'E:/github/flux-example/src', //绝对路径
        //自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
        extensions: ['', '.js', '.json', '.scss'],
        //模块别名定义,方便后续直接引用别名,无须多写长长的地址
        alias: {
            AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 即可
            ActionType : 'js/actions/ActionType.js',
            AppAction : 'js/actions/AppAction.js'
        }
    }

 

七 plugins 

插件是用来拓展webpack的功能的,会在整个构建过程中生效,执行相关的任务。Loadres和Plugins是完全不同的,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less),一次处理一个,插件并不是直接操作单个文件,它直接对整个构建过程起作用。webpack有很多内置插件和第三方插件。使用某个插件,先用npm安装,然后在webpack配置的plugins关键字部分添加该插件的一个实例。 
比如常用的HtmlWebpackPlugin,这个插件的作用是依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html。这在每次生成的js文件名称不同时非常有用(比如添加了hash值)。 
安装:

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

这个插件自动完成了我们之前手动做的一些事情,在正式使用之前需要对一直以来的项目结构做一些更改: 
(1)移除public文件夹,利用此插件,index.html文件会自动生成,此外CSS已经通过前面的操作打包到JS中了。 
(2)在app目录下,创建一个index.tmpl.html文件模板,这个模板包含title等必须元素,在编译过程中,插件会依据此模板生成最终的html页面,会自动添加所依赖的css,js,favicon等文件.

index.tmpl.html中的模板源代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
  </body>
</html>

(3)新webpack的配置文件,方法同上,新建一个build文件夹用来存放最终的输出文件

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/build",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
    },
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
        })
    ],
};

执行npm start,可以发现build文件夹下面生成了bundle.js和index.html
介绍一些常用的插件:

插件 作用
extract-text-webpack-plugin 第三方插件,将 CSS 打包成独立文件,而非内联。
HotModuleReplacementPlugin 代码热替换
HtmlWebpackPlugin 生成 HTML 文件,配合 ExtractTextPlugin 可以加入打包后的 js 和 css。
NoErrorsPlugin 报错但不退出 webpack 进程。
DefinePlugin 主要用来定义全局的环境变量,以便我们在自己的程序中引用它。

代码热替换 
代码热替换在开发的时候无需刷新页面即可看到更新,只在开发环境的配置文件使用,具体配置如下。

npm install --save-dev webpack-dev-server webpack-dev-middleware express

把webpack/hot/dev-server加入到 webpack 配置文件的 entry 项:

<!-- webpack.config.dev.js -->
entry: [
    'webpack-dev-server/client?http://localhost:3000',
    './src/app.js'
],

把new webpack.HotModuleReplacementPlugin()加入到 webpack 配置文件的 plugins 项:

<!-- webpack.config.js -->
plugins: [
new webpack.HotModuleReplacementPlugin()
]

在项目根目录新建个server.js文件,将server部分分离到一个单独的 :

<!-- server.js -->
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
var compiler = webpack(config);
var server = new WebpackDevServer(compiler, {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true,
stats: {
colors: true,
hash: false,
timings: true,
chunks: false,
chunkModules: false,
modules: false
}
});
server.listen(3000, 'localhost', function(err, result) {
if (err) {
return console.log(err);
}
console.log('Listening at http://localhost:3000/');
});

在 package.json 中定义启动监听热加载:

<!-- package.json -->
"scripts": {
  "start": "node server.js"
}

现在你可以通过运行npm start启动服务器。

 

posted @ 2018-03-08 21:56  LittleMoon  阅读(313)  评论(0编辑  收藏  举报