深刻浅出的webpack构建工具---DevServer配置项(二)

深刻浅出的webpack构建工具---DevServer配置项(二)css

阅读目录html

 摘要:webpack-dev-server是一个使用了express的Http服务器,它的做用主要是为了监听资源文件的改变,该http服务器和client使用了websocket通讯协议,只要资源文件发生改变,webpack-dev-server就会实时的进行编译。

1. contentBase

该配置项指定了服务器资源的根目录,若是不配置contentBase的话,那么contentBase默认是当前执行的目录,通常是项目的根目录。
可能如上解析还不够清晰,没有关系,咱们下面仍是先看下我整个项目的目录结构,而后进行相关的配置,使用contentBase配置项再来理解下:

### 目录结构以下:
demo1                                       # 工程名
|   |--- dist                               # dist是打包后生成的目录文件             
|   |--- node_modules                       # 全部的依赖包
|   |--- js                                 # 存放全部js文件
|   | |-- demo1.js  
|   | |-- main.js                           # js入口文件
|   |
|   |--- webpack.config.js                  # webpack配置文件
|   |--- index.html                         # html文件
|   |--- styles                             # 存放全部的css样式文件                              
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json

index.html 代码以下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link href="dist/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
  <div id="app"></div>
  <script src="dist/bundle.js"></script>
</body>
</html>

main.js 代码以下:

require('../styles/main.css');

import demo1 from './demo1.js';

demo1.js 代码以下:

console.log(111);

webpack配置代码以下:

const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
  entry: './js/main.js',
  output: {
    filename: 'bundle.js',
    // 将输出的文件都放在dist目录下
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/dist'
  },
  mode: 'development',
  module: {
    rules: [
      {
        // 使用正则去匹配要用该loader转换的css文件
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          // 转换 .css文件须要使用的Loader
          use: ['css-loader']
        })
      },
      {
        test: /\.(png|jpg)$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[ext]'
        }
      }
    ]
  },
  resolve: {
    // modules: ['plugin', 'js']
  },
  plugins: [
    new ExtractTextPlugin({
      // 从js文件中提取出来的 .css文件的名称
      filename: `main.css`
    })
  ]
};

package.json 配置代码以下:

"scripts": {
  "dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
  "build": "webpack --progress --colors"
}

运行 npm run dev后,一切正常成功后,在浏览器下 运行 http://localhost:8080/ 便可在控制台看到 打印出 111 了。

如上是没有使用devServer配置的状况下。 下面咱们来看下使用 devServer配置.

在webpack配置加上以下配置,即配置项指定了服务器资源的根目录。好比咱们打包后的文件放入 dist目录下。

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist")
  },
}

如上配置完成后,咱们再运行 npm run dev, 再在地址栏中 运行 http://localhost:8080/ 后看到以下信息:

也就是说 配置了 contentBase后,服务器就指向了资源的根目录,而再也不指向项目的根目录。所以再访问 http://localhost:8080/index.html 是访问不到的。可是访问 http://localhost:8080/bundle.js 该js文件是能够访问的到的。

2. port

该配置属性指定了开启服务器的端口号,好比以下配置:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081
  },
}

配置完成后,再运行打包命令 npm run dev 后,能够看到以下图所示:

如今咱们能够经过 以下地址 http://localhost:8081/ 也能够访问了,也就是说 经过port配置,端口号从默认的8080改为8081了。

3. host

该配置项用于配置 DevServer的服务器监听地址。好比想让局域网的其余设备访问本身的本地服务,则能够在启动DevServer时带上 --host 0.0.0.0.
host的默认值是 127.0.0.1, 下面咱们也简单的配置下 host 属性。

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0'
  }
}

配置完成后,再运行打包命令 npm run dev 后,能够看到以下图所示:

咱们访问 http://0.0.0.0:8081/  能够访问的到了,其余局域网的同窗应该也能访问的到吧。

4. headers

该配置项能够在HTTP响应中注入一些HTTP响应头。 好比以下:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    }
  }
}

如上配置完成后,打包下,刷新下浏览器,能够看到请求头加了上面的信息,以下所示:

5. historyApiFallback

该配置项属性是用来应对返回404页面时定向跳转到特定页面的。通常是应用在 HTML5中History API 的单页应用,好比在访问路由时候,访问不到该路由的时候,会跳转到index.html页面。
咱们如今在dist目录下 新建一个index.html, 代码以下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">欢迎大家来访问我</div>
</body>
</html>

为了使配置项生效,咱们只须要设置该 属性值为true便可; 以下配置:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    historyApiFallback: true
  },
}

如今咱们来访问 http://0.0.0.0:8081/home 这个不存在的路由时,会发生什么?以下所示:

如上能够看到,当不存在该路由的时候,经过该配置项,设置属性值为true的时候,会自动跳转到 index.html下。

固然如上只是简单的配置下,固然咱们也能够手动经过 正则来匹配路由,好比访问 /user 跳转到 user.html,访问 /home 跳转到 home.html, 以下配置:

固然咱们须要在 dist 目录下 新建 home.html 和 user.html 了,以下基本配置:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    historyApiFallback: {
      // 使用正则来匹配路由
      rewrites: [
        { from: /^\/user/, to: '/user.html' },
        { from: /^\/home/, to: '/home.html' }
      ]
    }
  },
}

从新运行打包下, 继续访问 http://0.0.0.0:8081/home 和 http://0.0.0.0:8081/user 便可看到能访问获得对应的页面了。

6. hot

该配置项是指模块替换换功能,DevServer 默认行为是在发现源代码被更新后经过自动刷新整个页面来作到实时预览的,
可是开启模块热替换功能后,它是经过在不刷新整个页面的状况下经过使用新模块替换旧模块来作到实时预览的。

咱们能够在 devServer中 配置 hot: true 便可:以下配置代码:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    historyApiFallback: {
      // 使用正则来匹配路由
      rewrites: [
        { from: /^\/user/, to: '/user.html' },
        { from: /^\/home/, to: '/home.html' }
      ]
    },
    hot: true
  }
}

固然咱们也能够在scripts命令行中配置,好比我项目中在package.json中的scripts配置以下:

"scripts": {
  "dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
  "build": "webpack --progress --colors"
}

7. inline

webpack-dev-server 有两种模式能够实现自动刷新和模块热替换机制。
1. iframe
页面是被嵌入到一个iframe页面,而且在模块变化的时候重载页面。

可能如上解释,咱们还不能彻底能理解究竟是什么意思,没有关系,咱们继续来看下配置和实践效果。

module.exports = {
  devServer: {
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    inline: false
  },
}

如上代码配置 inline: false 就是使用iframe模式来重载页面了。咱们的目录结构仍是上面的那种结构,而后咱们只须要在webpack中全部
配置以下:

const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
  entry: './js/main.js',
  output: {
    filename: 'bundle.js',
    // 将输出的文件都放在dist目录下
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/dist'
  },
  mode: 'development',
  module: {
    rules: [
      {
        // 使用正则去匹配要用该loader转换的css文件
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          // 转换 .css文件须要使用的Loader
          use: ['css-loader']
        })
      },
      {
        test: /\.(png|jpg)$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[ext]'
        }
      }
    ]
  },
  resolve: {
    // modules: ['plugin', 'js']
  },
  devServer: {
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    inline: false
  },
  plugins: [
    new ExtractTextPlugin({
      // 从js文件中提取出来的 .css文件的名称
      filename: `main.css`
    })
  ]
};

而后当咱们在命令行中,输入 webpack-dev-server 后 回车,能够看到以下图所示:

接着咱们在浏览器下 输入 http://0.0.0.0:8081/webpack-dev-server/ 地址后 回车,便可看到页面,咱们查看源代码的时候,会看到嵌入了一个iframe页面,以下图所示:

当咱们从新修改main.js 或 它的依赖文件 demo1.js 的时候,保存后,它也会自动从新加载页面,这就是使用 iframe 模式来配置加载页面的。

iframe 模式的特色有:
1. 在网页中嵌入了一个iframe,将咱们本身的应用代码注入到 这个 iframe中去了。
2. 在页面头部会有一个 App ready. 这个提示,用于显示构建过程的状态信息。
3. 加载了 live.bundle.js文件,还同时包含了 socket.io的client代码,进行了 websocket通信,从而完成了自动编译打包,页面自动刷新功能。

咱们看下请求的全部文件有以下:

2. inline 模式

开启模式,只须要把上面的配置代码变为 inline: true便可,它在构建变化后的代码会经过代理客户端来控制网页刷新。
如上配置后,咱们运行 webpack-dev-server 命令后,以下所示:

接着咱们在地址栏中 http://0.0.0.0:8081/ 运行下 就能够访问到 项目中的根目录 index.html了,当咱们修改入口文件的代码保存也同样
能实时刷新,其实效果是同样的。

inline模式的特色有:
1. 构建的消息在控制台中直接显示出来。
2. socket.io的client代码被打包进bundle.js当中,这样就能和websocket通信,从而完成自动编译工做,页面就能实现自动刷新功能。
3. 之后的每个入口文件都会插入上面的socket的一段代码,这样会使的打包后的bundle.js文件变得臃肿。

8. open

该属性用于DevServer启动且第一次构建完成时,自动使用咱们的系统默认浏览器去打开网页。

以下配置:

module.exports = {
  devServer: {
    // contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    // hot: true,
    inline: true,
    open: true
  }
}

设置 open: true 便可,当咱们运行完成 npm run dev 打包的时候,会自动打开默认的浏览器来查看网页。

9. overlay

该属性是用来在编译出错的时候,在浏览器页面上显示错误。该属性值默认为false,须要的话,设置该参数为true。
为了演示下,咱们来在main.js 代码内使用ES6的语法来编写代码,ES6是使用babel-loader 这样的来转化的,可是目前咱们的项目先不安装该loader,应该会报错的。好比在main.js 代码加以下一句代码:
const a;

配置 overlay: true便可:以下配置:

module.exports = {
  devServer: {
    // contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    // hot: true,
    inline: true,
    open: true,
    overlay: true
  }
}

运行 npm run dev 后,自动打开网页,显示以下所示:

10. stats(字符串)

该属性配置是用来在编译的时候再命令行中输出的内容,咱们没有设置 stats的时候,输出是以下的样子:以下所示:

该属性值能够有以下值:

stats: 'errors-only' 表示只打印错误,咱们添加下这个配置到devServer中;以下代码配置:

module.exports = {
  devServer: {
    // contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    // hot: true,
    inline: true,
    open: true,
    overlay: true,
    stats: 'errors-only'
  }
}

如今咱们继续 运行 npm run dev 后,会看到命令行中显示以下:

该配置的含义是 只有错误的才会被打印,没有错误就不打印,所以多余的信息就不会显示出来了。

该属性值还有 'minimal', 'normal', 'verbose' 等。

11. compress

该属性是一个布尔型的值,默认为false,当他为true的时候,它会对全部服务器资源采用gzip进行压缩。

12. proxy 实现跨域

有时候咱们使用webpack在本地启动服务器的时候,因为咱们使用的访问的域名是 http://localhost:8081 这样的,可是咱们服务端的接口是其余的,

那么就存在域名或端口号跨域的状况下,可是很幸运的是 devServer有一个叫proxy配置项,能够经过该配置来解决跨域的问题,那是由于 dev-server 使用了 http-proxy-middleware 包(了解该包的更多用法 )

假如如今咱们本地访问的域名是 http://localhost:8081, 可是我如今调用的是百度页面中的一个接口,该接口地址是:http://news.baidu.com/widget?ajax=json&id=ad。如今咱们只须要在devServer中的proxy的配置就能够了:
以下配置:

proxy: {
  '/api': {
    target: 'http://news.baidu.com', // 目标接口的域名
    // secure: true,  // https 的时候 使用该参数
    changeOrigin: true,  // 是否跨域
    pathRewrite: {
      '^/api' : ''  // 重写路径
    }
  }
}

所以全部的配置以下:

module.exports = {
  devServer: {
    // contentBase: path.join(__dirname, "dist"),
    headers: {
      'X-foo': '112233'
    },
    // hot: true,
    port: '8081',
    inline: true,
    open: true,
    overlay: true,
    stats: 'errors-only',
    proxy: {
      '/api': {
        target: 'http://news.baidu.com', // 目标接口的域名
        // secure: true,  // https 的时候 使用该参数
        changeOrigin: true,  // 是否跨域
        pathRewrite: {
          '^/api' : ''  // 重写路径
        }
      }
    }
  }
}

而后咱们在main.js里面编写以下代码:

import axios from 'axios';

axios.get('/api/widget?ajax=json&id=ad').then(res => {
  console.log(res);
});

在这里请求我使用 axios 插件,其实和jquery是一个意思的。为了方便就用了这个。

下面咱们来理解下上面配置的含义:

1. 首先是百度的接口地址是这样的:http://news.baidu.com/widget?ajax=json&id=ad;
2. proxy 的配置项 '/api' 和 target: 'http://news.baidu.com' 的含义是,匹配请求中 /api 含有这样的域名 重定向 到 'http://news.baidu.com'来。所以我在接口地址上 添加了前缀 '/api', 如: axios.get('/api/widget?ajax=json&id=ad'); 所以会自动补充前缀,也就是说,url: '/api/widget?ajax=json&id=ad' 等价
于 url: 'http://news.baidu.com/api/widget?ajax=json&id=ad'.
3. changeOrigin: true/false 还参数值是一个布尔值,含义是 是否须要跨域。
4. secure: true, 若是是https请求就须要改参数配置,须要ssl证书吧。
5. pathRewrite: {'^/api' : ''}的含义是重写url地址,把url的地址里面含有 '/api' 这样的 替换成 '',
所以接口地址就变成了 http://news.baidu.com/widget?ajax=json&id=ad; 所以就能够请求获得了,最后就返回
接口数据了。

以下图所示:

相关文章
相关标签/搜索