webpack中文网(七)内容安全策略、开发 - Vagrant、管理依赖(复习正则、require.context )、公共路径(public path)、集成(integrations)(NPM Scripts、Grunt、Gulp)
总结:
- 正则:JavaScript(二)(语法专题+标准库(Object Array console JSON))、错误处理机制、编程风格、控制台指令、RegExp(正则) 对象、修饰符_ChrisP3616的博客-CSDN博客
- 点字符
- 点字符(
. )匹配除回车(\r )、换行(\n ) 、行分隔符(\u2028 )和段分隔符(\u2029 )以外的所有字符。注意,对于码点大于0xFFFF 字符,点字符不能正确匹配,会认为这是两个字符。 - 位置字符
- 转义符
- 需要反斜杠转义的,一共有12个字符:
^ 、. 、[ 、$ 、( 、) 、| 、* 、+ 、? 、{ 和\ 。需要特别注意的是,如果使用RegExp 方法生成正则对象,转义需要使用两个斜杠,因为字符串内部会先转义一次。 - 量词符
? 问号表示某个模式出现0次或1次,等同于{0, 1} 。* 星号表示某个模式出现0次或多次,等同于{0,} 。+ 加号表示某个模式出现1次或多次,等同于{1,} 。 - 集成
- 概述
- 首先我们要消除一个常见的误解。webpack 是一个模块打包器(module bundler)(例如,Browserify 或 Brunch)。它不是一个任务执行器(task runner)(例如,Make, Grunt 或者 Gulp )。
- 任务执行器就是用来自动化处理常见的开发任务,例如项目的检查(lint)、构建(build)、测试(test)。相对于打包器(bundler),任务执行器则聚焦在偏重上层的问题上面。
- 打包器(bundler)帮助你取得准备用于部署的 JavaScript 和样式表,将它们转换为适合浏览器的可用格式。例如,JavaScript 可以压缩、拆分 chunk 和懒加载,以提高性能。打包是 web 开发中最重要的挑战之一,解决此问题可以消除开发过程中的大部分痛点。
- NPM Scripts(比grunt用的多)
- Grunt
- JavaScript 世界的构建工具
- 为何要用构建工具?
- 一句话:自动化。对于需要反复重复的任务,例如压缩(minification)、编译、单元测试、linting等,自动化工具可以减轻你的劳动,简化你的工作。当你在 Gruntfile 文件正确配置好了任务,任务运行器就会自动帮你或你的小组完成大部分无聊的工作。
- 为什么要使用 Grunt?
- Grunt 生态系统非常庞大,并且一直在增长。由于拥有数量庞大的插件可供选择,因此,你可以利用 Grunt 自动完成任何事,并且花费最少的代价。如果找不到你所需要的插件,那就自己动手创造一个 Grunt 插件,然后将其发布到 npm 上吧。先看看入门文档吧。
1. 内容安全策略
webpack 能够为其加载的所有脚本添加 nonce 。要启用此功能,需要在引入的入口脚本中设置一个 __webpack_nonce__ 变量。应该为每个唯一的页面视图生成和提供一个唯一的基于 hash 的 nonce,这就是为什么 __webpack_nonce__ 要在入口文件中指定,而不是在配置中指定的原因。请注意,nonce 应该是一个 base64 编码的字符串。
示例
在 entry 文件中:
__webpack_nonce__ = 'c29tZSBjb29sIHN0cmluZyB3aWxsIHBvcCB1cCAxMjM=';
启用 CSP
请注意,CSP 默认情况下不启用。需要与文档(document)一起发送相应的 CSP header 或 meta 标签 <meta http-equiv="Content-Security-Policy" ...> ,以告知浏览器启用 CSP。以下是一个包含 CDN 白名单 URL 的 CSP header 的示例:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com;
有关 CSP 和 nonce 属性的更多信息,请查看页面底部的进一步阅读部分。
进一步阅读
2. 开发 - Vagrant
如果你在开发一个更高级的项目,并且使用 Vagrant 来实现在虚拟机(Virtual Machine)上运行你的开发环境(development environment),那么你或许需要在虚拟机上运行 webpack。
项目配置
首先,确保 Vagrantfile 拥有一个固定 IP。
Vagrant.configure("2") do |config|
config.vm.network :private_network, ip: "10.10.10.61"
end
接下来便是在项目中安装 webpack 和 webpack-dev-server。
npm install --save-dev webpack webpack-dev-server
确保已经设好配置文件 webpack.config.js 。如果没有的话,下面的示例代码可以作为入门的简单配置:
module.exports = {
context: __dirname,
entry: "./app.js"
};
然后创建一个 index.html 文件。其中的 script 标签应当指向你的 bundle。如果 output.filename 没有在配置里设定,它的默认值便是 bundle.js 。
<!doctype html>
<html>
<head>
<script src="/bundle.js" charset="utf-8"></script>
</head>
<body>
<h2>Heey!</h2>
</body>
</html>
注意,你也需要创建一个 app.js 文件。
启动服务器
现在我们可以启动服务器了:
webpack-dev-server --host 0.0.0.0 --public 10.10.10.61:8080 --watch-poll
默认配置下,服务器只允许在它的本地访问。通过更改 --host 参数,便能够在我们的 PC 上访问它。
webpack-dev-server 会在 bundle 中加上一段连接 WebSocket 的脚本,一旦你的文件被更改,服务器便会重新加载应用。--public 参数便是为了告诉脚本从哪里去找 WebSocket。服务器默认使用 8080 端口,我们也需要在这里标明。
--watch-poll 确保 webpack 能够检测到文件的更改。默认配置下,webpack 会监听文件系统触发的相关事件,但是 VirtualBox 总会有这样或那样的问题。
现在服务器应该能够通过 http://10.10.10.61:8080 访问了。如果你改动了 app.js ,应用便会重新加载。
配合 nginx 的高级用法
为了更好的模拟类生产环境(production-like environment),还可以用 nginx 来代理 webpack-dev-server。
在你的 nginx 配置文件中,加入下面代码:
server {
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
error_page 502 @start-webpack-dev-server;
}
location @start-webpack-dev-server {
default_type text/plain;
return 502 "Please start the webpack-dev-server first.";
}
}
proxy_set_header 这几行配置很重要,因为它们关系到 WebSocket 的正确运行。
上一节中启动 webpack-dev-server 的命令可改为:
webpack-dev-server --public 10.10.10.61 --watch-poll
现在服务器只能通过 127.0.0.1 访问,这点关系不大,因为 ngnix 能够使得你的 PC 能访问到服务器。
小结
我们能够从固定 IP 访问 Vagrant box,然后由于公开了 webpack-dev-server,使浏览器可以直接访问到它。最后解决了 VirtualBox 不派发文件系统事件的常见问题,此问题会导致服务器不重新加载文件更改。
3. 管理依赖
es6 modules
commonjs
amd
带表达式的 require 语句
如果你的 request 含有表达式(expressions),会创建一个上下文(context),因为在编译时(compile time)并不清楚具体是哪一个模块被导入。
示例:
require("./template/" + name + ".ejs");
webpack 解析 require() 的调用,提取出来如下这些信息:
Directory: ./template
Regular expression: /^.*\.ejs$/
具有上下文的模块
(译者注:这里的 request 应该是指在 require() 语句中的表达式,如 “./template/” + name + “.ejs”)生成一个具有上下文的模块。它包含目录下的所有模块的引用(reference),这些模块能够「通过从 request 匹配出来的正则表达式」所 require 进来。上下文模块包含一个 map 对象,会把 request 中所有模块转译成对应的模块 id。
示例:
{
"./table.ejs": 42,
"./table-row.ejs": 43,
"./directory/folder.ejs": 44
}
上下文模块还包含一些运行时(runtime)逻辑来访问这个 map 对象。
这意味着 webpack 能够支持动态 require,但会导致所有可能用到的模块都包含在 bundle 中。
require.context
你还可以使用 require.context() 方法来创建自己的(模块)上下文。
你可以给这个方法传 3 个参数:要搜索的文件夹目录,是否还应该搜索它的子目录,以及一个匹配文件的正则表达式。
webpack 会在构建的时候解析代码中的 require.context() 。
语法如下:
require.context(directory, useSubdirectories = false, regExp = /^\.\//)
示例:
require.context("./test", false, /\.test\.js$/);
require.context("../", true, /\.stories\.js$/);
传递给 require.context 的参数必须是字面量(literal)!
上下文模块 API
一个上下文模块导出一个(require)函数,这个函数可以接收一个参数:request。
导出的方法有 3 个属性: resolve , keys , id 。
resolve 是一个函数,它返回请求被解析后得到的模块 id。keys 也是一个函数,它返回一个数组,由所有可能被上下文模块处理的请求(译者注:参考下面第二段代码中的 key)组成。
比如,如果想引入一个文件夹下面的所有文件,或者引入能匹配正则表达式的文件,你可以这样:
function importAll (r) {
r.keys().forEach(r);
}
importAll(require.context('../components/', true, /\.js$/));
var cache = {};
function importAll (r) {
r.keys().forEach(key => cache[key] = r(key));
}
importAll(require.context('../components/', true, /\.js$/));
id 是上下文模块里面所包含的模块 id. 它可能在你使用 module.hot.accept 的时候被用到。
4. 公共路径(public path)
webpack 提供一个非常有用的配置,该配置能帮助你为项目中的所有资源指定一个基础路径。它被称为公共路径(publicPath) 。
示例
这里提供一些示例,在实际应用中,这些示例的特性在实现的同时,还能保持高度整洁。
在构建项目时设置路径值
在开发模式中,我们通常有一个 assets/ 文件夹,它往往存放在和首页一个级别的目录下。这样是挺方便;但是如果在生产环境下,你想把这些静态文件统一使用CDN加载,那该怎么办?
想要解决这个问题,你可以使用有着悠久历史的环境变量。比如说,我们设置了一个名为 ASSET_PATH 的变量:
import webpack from 'webpack';
const ASSET_PATH = process.env.ASSET_PATH || '/';
export default {
output: {
publicPath: ASSET_PATH
},
plugins: [
new webpack.DefinePlugin({
'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH)
})
]
};
即时设定路径值
另一个可能出现的情况是,我们需要即时设置公共路径。webpack 提供一个全局变量供你设置,它名叫 __webpack_public_path__ 。所以在你的项目入口,你可以简单地设置如下:
__webpack_public_path__ = process.env.ASSET_PATH;
一切设置完成。因为我们已经在我们的配置项中使用了DefinePlugin , process.env.ASSET_PATH 就已经被定义了, 所以让我们能够安心地使用它了。
**警告:**请注意,如果你在入口文件中使用 ES6 模块导入,则在导入后对 __webpack_public_path__ 进行赋值。在这种情况下,你必须将公共路径(public path)赋值移至自己的专属模块,然后将其导入到你的 entry.js 之上:
import './public-path';
import './app';
5. 集成(integrations)
首先我们要消除一个常见的误解。webpack 是一个模块打包器(module bundler)(例如,Browserify 或 Brunch)。它不是一个任务执行器(task runner)(例如,Make, Grunt 或者 Gulp )。任务执行器就是用来自动化处理常见的开发任务,例如项目的检查(lint)、构建(build)、测试(test)。相对于打包器(bundler),任务执行器则聚焦在偏重上层的问题上面。你可以得益于,使用上层的工具,而将打包部分的问题留给 webpack。
打包器(bundler)帮助你取得准备用于部署的 JavaScript 和样式表,将它们转换为适合浏览器的可用格式。例如,JavaScript 可以压缩、拆分 chunk 和懒加载,以提高性能。打包是 web 开发中最重要的挑战之一,解决此问题可以消除开发过程中的大部分痛点。
好消息是,虽然有一些功能重复,但如果以正确的方式处理,任务运行器和模块打包器能够一起协同工作。本指南提供了关于如何将 webpack 与一些流行的任务运行器集成在一起的高度概述。
NPM Scripts
通常 webpack 用户使用 npm scripts 来作为任务执行器。这是比较好的开始。然而跨平台支持是一个问题,为此有一些解决方案。许多用户,但不是大多数用户,直接使用 npm scripts 和各种级别的 webpack 配置和工具,来应对构建任务。
因此,当 webpack 的核心焦点专注于打包时,有多种扩展可以供你使用,可以将其用于任务运行者常见的工作。集成一个单独的工具会增加复杂度,所以一定要权衡集成前后的利弊。
Grunt
对于那些使用 Grunt 的人,我们推荐使用 grunt-webpack 包(package)。使用 grunt-webpack 你可以将 webpack 或 webpack-dev-server 作为一项任务(task)执行,访问模板标签(template tags)中的统计信息,拆分开发和生产配置等等。如果你还没有安装过 grunt-webpack 和 webpack ,请先安装它们:
npm install --save-dev grunt-webpack webpack
然后注册一个配置并加载任务:
Gruntfile.js
const webpackConfig = require('./webpack.config.js');
module.exports = function(grunt) {
grunt.initConfig({
webpack: {
options: {
stats: !process.env.NODE_ENV || process.env.NODE_ENV === 'development'
},
prod: webpackConfig,
dev: Object.assign({ watch: true }, webpackConfig)
}
});
grunt.loadNpmTasks('grunt-webpack');
};
获取更多信息,请查看本仓库。
Gulp
在 webpack-stream 包(package)(也称作 gulp-webpack ) 的帮助下,也可以很简单方便的将 Gulp 与 webpack 集成。在这种情况下,不需要单独安装 webpack ,因为它是 webpack-stream 直接依赖:
npm install --save-dev webpack-stream
只需要把 webpack 替换为 require('webpack-stream') ,并传递一个配置文件就可以了:
gulpfile.js
var gulp = require('gulp');
var webpack = require('webpack-stream');
gulp.task('default', function() {
return gulp.src('src/entry.js')
.pipe(webpack({
}))
.pipe(gulp.dest('dist/'));
});
获取更多信息,请查看本仓库。
Mocha
mocha-webpack 可以用来将 Mocha 与 webpack 完全集成。这个仓库提供了很多关于工具优势和缺点方面的细节,但 mocha-webpack 还算是一层简单的封装,提供与 Mocha 几乎相同的 CLI,并提供各种 webpack 功能,例如改进了 watch 模式和优化了路径分析(path resolution)。这里是一个如何安装并使用它来运行测试套件的小例子(在 ./test 中找到):
npm install --save-dev webpack mocha mocha-webpack
mocha-webpack 'test/**/*.js'
获取更多信息,请查看本仓库。
Karma
karma-webpack 包(package)允许你使用 webpack 预处理 Karma 中的文件。它也可以使用 webpack-dev-middleware ,并允许传递两者的配置。下面是一个简单的示例:
npm install --save-dev webpack karma karma-webpack
karma.conf.js
module.exports = function(config) {
config.set({
files: [
{ pattern: 'test/*_test.js', watched: false },
{ pattern: 'test/**/*_test.js', watched: false }
],
preprocessors: {
'test/*_test.js': [ 'webpack' ],
'test/**/*_test.js': [ 'webpack' ]
},
webpack: {
},
webpackMiddleware: {
}
});
};
获取更多信息,请访问本仓库。
|