关键点: 在模块名之前添加一个 ~
符号来表明是要导入 npm 模块中的资源
格式为: ~npm模块名/path/to/file
例如
/* 导入 npm 模块 normalize.css 中的 css 文件 */
@import url(~normalize.css/normalize.css);
.test-npm-res {
/* 引用 npm 模块 ionicons 中的文件 */
background-image: url(~ionicons/dist/svg/ios-partly-sunny-outline.svg);
}
-
url(~module/image.png) => require('module/image.png')
-
@import rules in css doesn't look in node_modules folder
css
@import
is relative to the current directory. For resolving "like a module" you can prefix ~. -
Resolving module path when @import
as by default webpack interprets
@improt
within CSS as local imports because that's how CSS works. -
Loading Styles from node_modules Directory
The tilde character (~) tells webpack that it's not a relative import as by default. If tilde is included, it performs a lookup against node_modules
方式与在 CSS 文件中导入 npm 模块中的文件一致.
例如
<img src="~ionicons/dist/svg/ios-sunny.svg" width="50" height="50">
关键点: 导入时以模块的名字开头
格式为: 模块名/path/to/file
例如
// 导入 npm 模块 ionicons 中的文件
import svg from 'ionicons/dist/svg/ios-sunny-outline.svg';
例如需要在不同的环境下使用不同的图片
-
在
env.js
中配置好环境变量{ dev: { __qrcode__: './res/dev.jpg' }, prod: { __qrcode__: './res/prod.jpg' } }
-
通过
require
的方式使用环境变量引入图片var qrcode = require(__qrcode__);
注意不能使用
import
的方式来引入图片, 因为import
只接受静态的方式来引用资源
注意引入图片或者其他静态资源, 必须将其当作一个依赖来引入, 而非直接以一个字符串路径的方式来引入. 因为最终这个引用会构建生成, 例如生成一个带 hash 的路径, 或者打包成 base64
例如: var img = '/res/a.png';
, 应该修改为: import img from './res/a.png';
一般 CSS 文件的依赖我们会放在 CSS 中来管理, 例如我们会在 CSS 文件中导入第三方的 CSS 文件
@import url(~normalize.css);
.body--index {
background-color: #eee;
}
这样做的好处就是上层使用者不需要关心下层的依赖关系, 但这种方式无法将第三方的 CSS 抽离出来.
打包的结果可想而知, 只会产生一个包含了所有样式的 CSS 文件, 任何样式的修改, 都会导致这个文件变化, 不利于缓存.
因此我们想把第三方的 CSS 抽离出来, 形成一个 vendor.css
, 这个文件是相对稳定的, 因此可以更好的利用缓存.
关键点:
-
不要在 CSS 文件中通过
@import
方式来导入第三方的 CSS 依赖 -
在 JS 文件中通过
import
方式来导入第三方的 CSS 依赖 -
修改提取出
vendor
的CommonsChunkPlugin
来支持自动打包第三方的 CSS 依赖/\.(js|json|css)$/.test(module.resource)
例如
/* index.css */
/*@import url(~normalize.css);*/
.body--index {
background-color: #eee;
}
// index.js
import 'normalize.css';
import './index.css';
-
安装依赖
npm install less-loader less
-
配置
less-loader
// 找出内置的 css 文件关联的 loader, 然后添加上 less-loader 来处理 less 文件 var cssRule = webpackConfig.module.rules.filter(function(rule) { return rule.test.test('.css'); })[0]; var lessLoader = cssRule.use.slice(); var less = { loader: 'less-loader', options: { sourceMap: env.__mode__ == 'dev' ? true : false, // 全局覆盖 less 变量, 对定制主题非常有用 // this puts the declaration at the end of your base file, // meaning it will override anything defined in your Less file. // http://lesscss.org/usage/#using-less-in-the-browser-modifyvars modifyVars: { '@var': '#aaa' } } }; lessLoader.push(less); // 添加 less-loader 配置 webpackConfig.module.rules.push({ test: /\.less$/, use: lessLoader });
-
使用
less
// 注意导入 less 不需要使用 url() 这样的语法 @import "./hello.less";
注意:
vue-loader
虽然默认支持处理 less
文件, 但如果在 less
中 @import
了 css 文件, 就需要自己配置一下 less
, 将 css-loader
的 importLoaders
配置为 1
, 否则通过 @import
导入的 css 不会经过 postcss
的处理
例如
// 找出内置的 css 文件关联的 rule
var cssRule = webpackConfig.module.rules.filter(function(rule) {
return rule.test.test('.css');
})[0];
// 找出内置的 css-loader, 这样便于共用配置
var cssLoader = cssRule.use.filter(function(loader) {
return loader.loader == 'css-loader';
})[0];
var lessLoader = cssRule.use.slice();
var less = {
loader: 'less-loader',
options: {
sourceMap: env.__mode__ == 'dev' ? true : false,
// 全局覆盖 less 变量, 对定制主题非常有用
// this puts the declaration at the end of your base file,
// meaning it will override anything defined in your Less file.
// http://lesscss.org/usage/#using-less-in-the-browser-modifyvars
modifyVars: {
'@var': '#aaa'
}
}
};
lessLoader.push(less);
// 为了样式支持 HMR, 在开发环境下只使用 style-loader
// XXX 目前发现 async import 即动态导入的模块 HMR 没有生效,
// 即开启 HMR 的时候, 修改 async-component.vue 确实会重载模块, 但界面上没有效果
var vueCssLoader = env.__mode__ == 'dev' ? [{
loader: 'vue-style-loader'
}, cssLoader] : ExtractTextPlugin.extract({
fallback: 'vue-style-loader',
use: [cssLoader]
});
var vueLessLoader = vueCssLoader.slice();
vueLessLoader.push(less);
// vue-loader 配置
options: {
loaders: {
css: vueCssLoader,
less: vueLessLoader
}
}
PS: 支持 SASS
的方式类似, 只是需要安装的依赖是 sass-loader node-sass
, 然后将上面 less-loader
的地方都改成 sass-loader
即可
-
方式一: 将整个 manifest chunk inline 到入口的 HTML 文件中
需要先 split out the runtime code, 通过
CommonsChunkPlugin
将 manifest chunk 提取出来new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', filename: wpkConfig.output.chunk + '/' + wpkConfig.output.jsFilename, minChunks: Infinity })
这样 manifest 会包含
webpackBootstrap
的代码, 但其中变的部分其实很少, 还有优化空间, 再使用如下插件之一将 manifest inline 到入口的 HTML 文件中-
inline-manifest-webpack-plugin
还需要在 HTML 中使用
<%= htmlWebpackPlugin.files.webpackManifest %>
来明确输出 manifest 分块的内容 -
inline-chunks-html-webpack-plugin
直接找到需要 inline 的 chunk 的 HTML 标签, 然后替换掉标签的内容, 因此无需自己再明确指定输出
-
-
方式二: 将 chunk map inline 到 HTML 中
inline-chunk-manifest-html-webpack-plugin
将每个 chunk ID 和文件名映射起来提取出来, 然后修改webpackBootstrap
直接使用这个映射, 这样就可以将webpackBootstrap
模块合并到vendor
分块, 不需要单独提取出 manifest chunk 了.new InlineChunkManifestHtmlWebpackPlugin({ dropAsset: false })
得到的结果类似于
<script> window.webpackManifest={"0":"chunk/2356817.js","1":"chunk/427f17d.js","2":"chunk/2daed49.js"} </script>
// webpackBootstrap script.src = __webpack_require__.p + window["webpackManifest"][chunkId];
但是每次其他模块的代码, 竟然影响到了 vendor 的 hash?
因为 vendor 原本包含了 manifest 的内容, 代码修改后 manifest 的内容肯定会变, vendor 的 hash 也就变了, 但是
InlineChunkManifestHtmlWebpackPlugin
替换了 vendor 文件的内容后, 没有重新计算 hash, 因此前后生成的vendor.js
内容是一样的, 但是 hash 却不一样注意
webpackBootstrap
代码可能会变化的地方-
当 chunk 数量变化时会造成
installedChunks
变化以下途径会产生新的 chunk
entry
定义的每一个入口- 动态
import
或者require.ensure
产生的异步分块 CommonsChunkPlugin
提取的通用分块, 如果只提取了 vendor 分块, 就不会影响installedChunks
的数量, 因为 vendor 作为初始分块, 不计入installedChunks
中
-
加载异步分块的方法中放置了每个分块对应的文件路径
// __webpack_require__.e // 分块文件 URL script.src = __webpack_require__.p + "chunk/" // 分块ID与分块名称的映射: 分块ID 定义在 `webpackJsonp([2]` // 先通过 chunkId (分块ID默认是一个索引数字), 找出 chunk 的名称 // 如果是动态 import 进来的分块, 没有对其命名的话, 就不会出现在这个映射中, // 则直接使用 chunkId 作为分块名称 + ({"0":"abc","1":"ddd","2":"index"}[chunkId]||chunkId) // 分块ID与分块 hash 的映射 + "-" + {"0":"2356817","1":"427f17d","2":"2daed49"}[chunkId] + ".js";
-
所有静态资源的根路径
__webpack_require__.p = "//cdn.com/path/";
-
如果要同时使用 dynamic import 和 babel-loader
来转译 es2015
例如:
import(/* webpackChunkName: "abc" */'./abc.js').then(function(mod) {
mod['default']();
});
必须给 babel-loader
配置 syntax-dynamic-import
插件来支持 import()
语法, 否则会报错提示
SyntaxError 'import' and 'export' may only appear at the top level
或者你可以不使用 dynamic import 功能, 使用以往的 require.ensure
来动态加载模块, 这样就不需要给 babel-loader
配置 syntax-dynamic-import
插件了
在联调阶段, 由于后端接口都已经开发完成, 此时前端构建好的代码也会合并到后端项目, 一起部署到测试服务器上. 这时候往往会暴露出许多问题, 因为 mock 数据接口相对于真实实现的接口, 输出的数据会比较理想. 此时如果发现问题, 调试起来就比较麻烦, 会需要重复以下步骤, 效率很低, 而且影响到测试环境的稳定性
- 前端构建
- 提交到后端
- 重新部署到测试服务器看效果
因此前端需要能够将 mock 数据接口切换成测试服务器上的接口, 这样就好定位和调试问题, 不用反复和后端沟通和部署测试环境.
-
启动时修改环境变量
__api_root_endpoint__
的值, 指向//api.domain.com
npm start -- --env.__api_root_endpoint__=//api.domain.com
可以将其封装成一个
npm scripts
便于以后执行"start-test": "npm start -- --env.__api_root_endpoint__=//api.domain.com"
-
修改浏览器的安全设置, 允许跨域访问
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir
-
先正常登录后端系统
访问
http://api.domain.com/login
-
登录成功后, 正常访问页面即可
访问
http://localhost:8004/h5/map/map