Skip to content

Latest commit

 

History

History
355 lines (259 loc) · 12.3 KB

FAQ.md

File metadata and controls

355 lines (259 loc) · 12.3 KB

FAQ

如何在 CSS 文件中导入 npm 模块(node_modules 文件夹)中的文件?

关键点: 在模块名之前添加一个 ~ 符号来表明是要导入 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);
}

如何在 HTML 文件中导入 npm 模块(node_modules 文件夹)中的文件?

方式与在 CSS 文件中导入 npm 模块中的文件一致.

例如

<img src="~ionicons/dist/svg/ios-sunny.svg" width="50" height="50">

如果在 JS 文件中导入 npm 模块(node_modules 文件夹)中的文件?

关键点: 导入时以模块的名字开头

格式为: 模块名/path/to/file

例如

// 导入 npm 模块 ionicons 中的文件
import svg from 'ionicons/dist/svg/ios-sunny-outline.svg';

如何在 JS 中根据环境动态的引入静态资源

例如需要在不同的环境下使用不同的图片

  • 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 文件: vendor.css?

一般 CSS 文件的依赖我们会放在 CSS 中来管理, 例如我们会在 CSS 文件中导入第三方的 CSS 文件

@import url(~normalize.css);

.body--index {
    background-color: #eee;
}

这样做的好处就是上层使用者不需要关心下层的依赖关系, 但这种方式无法将第三方的 CSS 抽离出来.

打包的结果可想而知, 只会产生一个包含了所有样式的 CSS 文件, 任何样式的修改, 都会导致这个文件变化, 不利于缓存.

因此我们想把第三方的 CSS 抽离出来, 形成一个 vendor.css, 这个文件是相对稳定的, 因此可以更好的利用缓存.

关键点:

  • 不要在 CSS 文件中通过 @import 方式来导入第三方的 CSS 依赖

  • 在 JS 文件中通过 import 方式来导入第三方的 CSS 依赖

  • 修改提取出 vendorCommonsChunkPlugin 来支持自动打包第三方的 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';

如何支持 less 文件?

  • 安装依赖

    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-loaderimportLoaders 配置为 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 即可

提取出 webpack 的 runtime/manifest 的方式

  • 方式一: 将整个 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 代码可能会变化的地方

    1. 当 chunk 数量变化时会造成 installedChunks 变化

      以下途径会产生新的 chunk

      • entry 定义的每一个入口
      • 动态 import 或者 require.ensure 产生的异步分块
      • CommonsChunkPlugin 提取的通用分块, 如果只提取了 vendor 分块, 就不会影响 installedChunks 的数量, 因为 vendor 作为初始分块, 不计入 installedChunks
    2. 加载异步分块的方法中放置了每个分块对应的文件路径

      // __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";
    3. 所有静态资源的根路径

      __webpack_require__.p = "//cdn.com/path/";

dynamic import 使用注意

如果要同时使用 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 数据接口切换成测试服务器上的接口, 这样就好定位和调试问题, 不用反复和后端沟通和部署测试环境.

解决方式

  1. 启动时修改环境变量 __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"

  2. 修改浏览器的安全设置, 允许跨域访问

    "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir

  3. 先正常登录后端系统

    访问 http://api.domain.com/login

  4. 登录成功后, 正常访问页面即可

    访问 http://localhost:8004/h5/map/map