Webpack(二)

697 阅读4分钟

写在前面

上一篇博客,我们详细说了模块化的历史,不使用webpack的坏处,webpack的作用,以及怎么安装,配置webpack,配置webpack是在webpack.config.js文件中完成的


五、使用webpack

有三种方式运行webpack

1. 执行 npx webpack,会将我们的脚本作为入口起点,然后 输出 为 main.js。Node 8.2+ 版本提供的 npx 命令,可以运行在初始安装的 webpack 包(package)的 webpack 二进制文件(./node_modules/.bin/webpack)

2. 输入./node_modules/.bin/webpack

3. 用 CLI 这种方式来运行本地的 webpack 不是特别方便,我们可以设置一个快捷方式。在package.json添加一个 npm 脚本(npm script),package.json里的命令和npx类似自动在相应目录下查找

  "scripts": {
        "webpack"
    }    

现在,可以使用 npm run build 命令,来替代我们之前使用的 npx 命令。


注意现在我们的文件结构

  webpack-demo
  |- package.json
  |- /dist
    |- index.html
  |- /src
    |- index.js

我们把src里的index.js,打包至dist目录下的main.js(webpack.config.js里规定的entry和output),我们需要在dist目录下创建一个index.html,而且index.html需要引用main.js,而不是index.html,这样很不方便下面我们解决这个问题,并且使用hash值作为output的文件名

六、index.html的引用问题

var path = require('path');

module.exports = {  
    mode: 'development',  
    entry: './src/index.js',  
    output: {    
        path: path.resolve(__dirname, 'dist'),   
        filename: '[name].[contenthash].js'      
    },
上面代码是分成常见的webpack.config.js的设置,注意output的文件名,这样我们输出的buddle就会是原本文件名+哈希数字,

但是这样会导致每次 npm run build 就会产生一个新的buddle

我们可以修改package.json里的script,来同时完成删除旧的生成新的

  "scripts": {
        "build": "rm -rf dist && webpack"
    }   

七、插播HTTP缓存(哈希名字的目的)

举个例子,比如我们访问一个网站,需要下载HTML以及很多CSS,图片,JS等等文件,很慢,下次我们访问该网站时候,我们首先下载HTML,然后就会在内存和硬盘里查找附属的文件,那么就会有下面疑问?

1. index.hmtl必须要下载吗?

2. 网站更新怎么办?

3. 怎么设置缓存?

我们可以在响应头里设置 cache .control:public, max-age-31536000,那么网站就可以缓存了

如果网站更新,那么新buddle的哈希文件名也会改变,所以只有内容不一样,文件名也不一样,

结合上一点就可以回答index.html不缓存了,如果index.html缓存了,那么就无法得知是否更新了,只能等待缓存失效,


八、自动生成HTML

上面我们讲的都是输入一个js文件,根据依赖输出一个js文件,而且我们必须手动在dist目录下创建html,手动引入,但是我们使用哈希文件名就破裂了


var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');

module.exports = {  
    mode: 'development',  
    entry: './src/index.js',  
    output: {    
        path: path.resolve(__dirname, 'dist'),    
        filename: '[name].[contenthash].js'  
    },  
    plugins: [new HtmlWebpackPlugin()]};
我们使用插件,该插件会在dist目录下(output规定)自动生成HTML文件,而且会自动引入生成的js文件(buddle),该插件接受参数,template:"路径",接受一个模板,来生成HTML


九、引入CSS

上面我们解决了JS和HTML,现在我们解决CSS

模块化css,我们通常把import或者require xxx.css放在js文件中,如果没有做任何设置,那么webpack遇到css后缀就会报错,我们使用loader来打包模块化的css后缀文件

module.exports = {
  module: {
    rules: [
      { test: /\.css$/, use: ['style-loader','css-loader'] }
    ]
  }
};
“嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.css' 的路径」时,在你对它打包之前,先用css-loader 把css文件读到js里面。然后再使用style-loader 把它插到head的style标签里面

十、webpack-dev-serve

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

webpack.config.js修改配置文件,告诉开发服务器(dev server),在哪里查找文件:

  module.exports = {
   devServer: {
     contentBase: './dist'
   }
  }

以上配置告知 webpack-dev-server,在 localhost:8080 下建立服务,将 dist 目录下的文件,作为可访问文件。

package.json让我们添加一个 script 脚本,可以直接运行开发服务器(dev server):

    "scripts": {
      "start": "webpack-dev-server --open", //open指打开浏览器
    },

webpack-dev-serve是不生成dist的

十一、抽出CSS文件

const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("styles.css"),
  ]
}

它会将所有的入口 chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件。因此,你的样式将不再内嵌到 JS bundle 中,而是会放到一个单独的 CSS 文件(即 styles.css)当中。 如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。

new ExtractTextPlugin(options: filename | object)

生成文件的文件名。可能包含 [name], [id] and [contenthash]
警告: ExtractTextPlugin每个入口 chunk 都生成一个对应的文件,所以当你配置多个入口 chunk 的时候,你必须使用 [name], [id][contenthash]


十二、两种webpack.config.js

创建两个webpack.config.js,一个开发时用用loader且不用hash,另一个生产时候用css extract以及hash,

  "scripts": {
        "build": "rm -rf dist && webpack" --config webpack.connfig.xxx.json
    } 

这样即可

我们甚至看用继承的思想,因为两个json文件大部分都一样,创建一个新的json文件,再require进来即可

const base = require( './webpack. config.base.js') 
module.exports = { 
    ...base,
    devServer: {
        contentBase:"./dist"
    }
    module: {  
        rules: [
            {
            test: /\.css$/i ,
            use: ["style-loader", "css-loader"]
         }
        ]
    }
}; 

...base把base所有东西都抄过来,然后下面在进行覆盖,这样很容易有bug,比如module一不小心把base里的全都覆盖了

const base = require( './webpack. config.base.js') 
module.exports = { 
    ...base,
    devServer: {
        contentBase:"./dist"
    }
    module: {  
    ...base.module
        rules: [
            {
            test: /\.css$/i ,
            use: ["style-loader", "css-loader"]
         }
        ]
    }
}; 

正确应该上面代码做

也可以if else写入一个文件中

package.json

{
  "scripts": {
    "start": "webpack-dev-server",
    "build": "webpack"
  },
  // ...
}

webpack.config.js

var path = require('path');
var merge = require('webpack-merge');

var TARGET = process.env.npm_lifecycle_event;

var common = {
  entry: path.join(__dirname, 'app'),
  ...
  module: {
    loaders: [
      {
        test: /\.css$/,
        loaders: ['style', 'css'],
      },
    ],
  },
};

if(TARGET === 'start') {
  module.exports = merge(common, {
    module: {
      // loaders will get concatenated!
      loaders: [
        {
          test: /\.jsx?$/,
          loader: 'babel?stage=1',
          include: path.join(ROOT_PATH, 'app'),
        },
      ],
    },
    ...
  });
}

if(TARGET === 'build') {
  module.exports = merge(common, {
    ...
  });
}

...