webpack学习笔记

webpack: 模块打包器

webpack支持AMD和CommonJS,以及其他的一些模块系统,并且兼容多种JS书写规范(如:.coffee,.less),可以处理模块间的依赖关系,所以具有更强大的JS模块化的功能,它能对静态资源进行统一的管理以及打包发布。在 webpack 里,所有类型的文件都可以是模块,包括我们最常见的 JavaScript,及 CSS 文件、图片、json 文件等等。通过 webpack 的各种加载器,我们可以更高效地管理这些文件。

Webpack 会分析入口文件,解析包含依赖关系的各个文件。这些文件(模块)都打包到 bundle.js 。Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块。在页面启动时,会先执行 entry.js 中的代码,其它模块会在运行 require 的时候再执行。

最简单的webpack.config.js:

module.exports = {
 entry: './main.js',  // 入口文件,常写成path.resolve(__dirname,'src/app.js') , path需要提前require('path')
 output: {
  path: __dirname,  
  filename: 'bundle.js'  // 输出文件的目录和名称
 }
}

执行./node_modules/.bin/webpack命令(webpack --progress --colors通过参数让编译的输出内容带有进度和颜色,--watch参数可以开启监听模式,实现修改后自动增量编译(非自动刷新),--display-error-details打印错误详情,-p会针对发布环境编译也就是会压缩代码,package.json中的脚本部分已经默认在命令前添加了node_modules/.bin路径,所以无论是全局还是局部安装的Webpack,不需要写前面那指明详细的路径。),就会生成bundle.js(在index.html中事先引入<script src='./bundle.js'></script>)。目前为止,其实webpack啥也没做,bundle.js和main.js也没啥区别,也不能再main.js中写import '.../文件',因为浏览器目前还不支持import。借助webpack的loader:babel-loader就可以实现es6的加载方式了。

babel-loader:

安装 babel-loader:

npm install babel-loader babel-core babel-preset-es2015 --save-dev

配置 webpack.config.js的module字段:

module: {
      loaders: [{
          test: /\.js$/, // babel内置对jsx的支持,所以也可写成test: /\.js|jsx$/,
          loaders: ['babel-loader?presets[]=es2015'],
          exclude: /node_modules/
      }]
 }

webpack还有一个配置项:resolve: {extensions: ['', '.js', '.jsx'} 用来指定import时可以被省略的文件后缀。

webpack的配置项output下的publicPath就是对应着最终在index.html文件中引用打包好的文件的目录。

在开发阶段为了便于调试,可以打包后的文件配置成source maps,对应的配置项为:devtool: DEBUG ? 'cheap-module-eval-source-map' : false,

webpack的配置文件可以根据环境而不同,如webpack.dev.config.js和webpack.prod.config.js,运行命令:webpack --config webpack.dev.config.js

为了能实时监控文件变化并反应到浏览器上,Webpack还提供了一个基于Node.js Express框架的开发服务器,它是一个静态资源Web服务器,在开发过程中,开发服务器会监听每一个文件的变化,进行实时打包,并且可以推送通知前端页面代码发生了变化,从而可以实现页面的自动刷新。安装webpack-dev-server,执行命令webpack-dev-server --config webpack.dev.config.js。

webpack也可以把多个文件单独打包,以便在多个页面中单独使用。(Webpack 中涉及路径配置最好使用绝对路径,建议通过 path.resolve(__dirname, "app/folder") 或 path.join(__dirname, "app", "folder") 的方式来配置,以兼容 Windows 环境。)

module.exports = {
  context: __dirname + "/src", // context表示webpack从context指定的文件夹开始寻找entry里的文件
  entry: {
    // app: ["./home.js", "./events.js", "./vendor.js"], 这表示多个文件打包成一个文件:app.bundle.js
    home: "./home.js",
    events: "./events.js",
    contact: "./contact.js",
  },
  output: {
    path: __dirname + "/dist",
    filename: "[name].bundle.js",
  },
};

加载器:

加载器是webpack准备的一些预处理工具,如用来处理jsx,es6,sass文件等。Webpack 本身只能处理原生的 JavaScript 模块,但是 loader 转换器可以将各种类型的资源转换成 JavaScript 模块。它本身是一个函数,接受源文件作为参数,返回转换的结果。这样,任何资源都可以成为 Webpack 可以处理的模块。按照惯例,而非必须,loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 json-loader。在引用 loader 的时候可以使用全名 json-loader,或者使用短名 json。这个命名规则和搜索优先级顺序在 webpack 的 resolveLoader.moduleTemplates api 中定义。

Default: ["*-webpack-loader", "*-web-loader", "*-loader", "*"]

如编译jsx和es6到原生js,需要按照依赖:babel-loader, babel-core, babel-preset-es2015, babel-preset-react, webpack中的loader配置如下:

module: {
  loaders: [{
    test: /\.jsx?$/,
    include: [path.resolve(__dirname, '../src')],
    //exclude: [path.resolve(__dirname, '../node_modules')]
    loader: 'babel', // babel是babel-loader的缩写,-loader都可以不写的
    query: {          // 为loader提供额外的设置选项(可选)
      presets: ['es2015', 'react']
    }
  }]
}

Babel其实可以完全在webpack.config.js中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。因此提取出相关部分,分两个配置文件进行配置(webpack会自动调用.babelrc里的babel配置选项),配置.babelrc中{"presets": ["es2015","react"]}

加载css:

Webpack允许像加载任何代码一样加载 CSS。加载 CSS 需要 css-loader 和 style-loader,他们做两件不同的事情,css-loader会遍历 CSS 文件,然后找到 url() 和@import表达式然后处理他们,style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中。安装css-loader和style-loader后,配置文件中添加loader:

{
  test: /\.css$/,
  loader: 'style!css'  //有多个加载器,中间用感叹号隔开,多个加载器从右往左执行
}

加载sass:

安装sass-loader, node-sass,配置{test: /\.(sass|scss)$/, loader: 'style!css!sass'}。

转换小图片为base64字符串:

安装url-loader,配置图片如果不大于 25KB 的话要自动在它从属的 css 文件中转成 BASE64 字符串: {test: /\.(png|jpeg|gif|jpg)$/,loader: 'url?limit=25000'}

处理大图片:

安装file-loader,配置{test: /\.scss$/, loader: 'file?name=images/[name].[ext]'},把图片放到指定目录中。

其他loader:

如使用postcss-loader结合autoprefixer可以自动给css属性加上浏览器前缀。

插件:

插件是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务,可以完成更多 loader 不能完成的功能,使用时在 webpack 的配置信息 plugins 选项中指定。Webpack 本身内置了一些常用的插件,也可以通过 npm 安装第三方插件。Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

 压缩插件:这是webpack自带的插件,压缩时可以选择忽略代码中的警告: new webpack.optimize.UglifyJsPlugin({compress: {warnings: false} })

设置全局变量:new webpack.DefinePlugin({TESTENV: JSON.stringify(JSON.parse(process.env.BUILD_TESTENV))}),在应用代码里就可以使用TESTENV了:if(!TESTENV){...},执行命令BUILD_TESTENV=true webpack就可以根据实际情况设置TESTENV了,并且webpack -p会执行uglify dead-code elimination, 任何这种代码都会被剔除, 不用担心打包出不必要/合适的代码。

提取css插件:

在webpack中编写js文件时,可以通过require的方式引入其他的静态资源如css文件,可通过loader对文件自动解析并打包文件。通常会将js 文件打包合并,css文件会在页面的header中嵌入style的方式载入页面。但开发过程中我们并不想将样式打在脚本中,最好可以独立生成css文件,以外链的形式加载。这时extract-text-webpack-plugin插件可以帮我们达到想要的效果,将js中的css文件提取,并以指定的文件名来进行加载。

安装依赖:npm install --save-dev extract-text-webpack-plugin

修改loader和plugins配置:

const ExtractTextPlugin = require("extract-text-webpack-plugin");
//
loaders:[
    {test: /\.css$/,  loader: ExtractTextPlugin.extract('style-loader,'css-loader'}
]
plugins: [
    new ExtractTextPlugin('app.css')
]
//也可以分离出多个文件
const extractCSS = new ExtractTextPlugin('stylesheets/[name]-one.css');
const extractLESS = new ExtractTextPlugin('stylesheets/[name]-two.css');
loaders:[
    {test: /\.css$/,  loader: extractCSS.extract('css-loader,'postcss-loader'},
    {test: /\.less$/,  loader: extractLESS.extract('css-loader,'less-loader'}
]
plugins: [
    extractCSS,
    extractLESS
]

html-webpack-plugin插件:

(根据指定的模板文件)生成html5文件,并自动插入webpack生成的scripts和css文件文件。 

react-transform-hrm插件:

react里结合babel实现热加载HMR(hot module replacement,也就是自动刷新),npm安装插件后,webpack的配置文件plugins中添加:new webpack.HotModuleReplacementPlugin(),配置babel-loader的query选项:

query: {
    presets: ['es2015', 'react'],
    plugins: [
       ['react-transform', {
            transforms: [{
              transform: 'react-transform-hmr',
              imports: ['react'],
              locals: ['module']  
            }]
          }
       ]  
    ]
}

分离应用和第三方代码:

当你的应用依赖其他库尤其是像 React JS 这种大型库(或第三方的体积达到整个应用的 20% 或者更高)的时候,你需要考虑把这些依赖分离出去,这样就能够让用户在你更新应用之后不需要再次下载第三方文件。

修改entry和plugins来分离文件:

entry:{
  app: path.resolve(__dirname, 'src/app.js'),
  vendors: ['react', 'react-dom']
}
......
plugins: [
  // name指向entry中verdors属性
  // filename中的文件会自动构建到output中的path属性下面
  new webpack.optimize.CommonsChunkPlugin({name: 'vendors',filename:'vendors.js'})
]

这样在output.path下会生成两个js文件,bundle.js和vendors.js。

多入口文件下,还可以通过new webpack.optimize.CommonsChunkPlugin('common.js')打包出共用模块,还可以指定共用模块在不同文件里import的次数达到多少时才打包进common.js。

posted @ 2017-06-22 17:29  荔枝龙眼  阅读(313)  评论(0编辑  收藏  举报

这里是页脚Html代码