动态模块导入是主流浏览器最新的 JavaScript 特性之一. 该特性的主要用例是延迟加载模块, 允许在需要时交付内容, 而不是一次交付所有内容.
在本文中, 我将用几行代码演示如何使用 vue.js 构建一个延迟加载路由器. 这将在已经实现动态模块导入的浏览器中本地工作, 但我还将为较老的浏览器提供一个备份.
在 GitHub https://github.com/anthonygore/vue-dynamic-import 上抓取完成的代码.
静态 JavaScript 模块导入
如果您正在使用任何主流浏览器的最新版本, 那么您现在就可以本地执行静态导入 / 导出. Vue. 这意味着你可以从这样的文件中导出组件定义:
BooksPage.JS
- export default {
- name: 'BooksPage',
- template: `
- <div>
- <h1>Books Page</h1>
- <p>{{ message }}</p>
- </div>
- `,
- data() {
- return {
- message: 'Oh hai from the books page'
- }
- }
- };
然后像这样导入到你的应用里:
App.JS
- import BooksPage from './BooksPage';
- new Vue({
- components: {
- BooksPage
- }
- });
创建组件 JavaScript 模块允许您组织您的应用程序, 使每个 "页面" 都在一个不同的文件中. 如果您使用 Vue.JS 单文件组件, 这并不是什么新鲜事, 但是现在有了本地支持, 这个架构可以在没有 webpack 或 Babel 的情况下实现.
动态 JavaScript 模块导入
如果组件表示页面, 则最好按需获取模块文件, 这样用户就不必下载他们没有访问过的页面. 不过, 静态导入在编译时解析. 这意味着你不能从'./BooksPage'导入 BooksPage; 在 if 语句中有条件地加载它. 相反, 您的静态导入将在加载它们的脚本运行时立即开始下载.
这就是动态导入的作用. 这些可以在运行时确定, 这意味着您可以根据需要有条件地加载 JavaScript 模块, 从而加载页面. 注意, 动态导入返回一个承诺, 它解析模块内容.
- import HomePage from './pages/HomePage.js';
- Vue.component('my-router', {
- template: '<component :is="page"/>',
- data() {
- return {
- page: HomePage
- }
- },
- methods: {
- navigate(name) {
- this.page = return () => import(`./${name}Page.js`)
- }
- }
- });
注意: component 是一个内置的 "元" 组件, 它通过 prop 接受组件定义 is. 与所有 Vue 组件一样, 它可以是组件定义, 也可以是解析组件定义的 Promise.
如果您正在使用最新的 Safari 或 Chrome Canary 浏览器, 这段代码将在浏览器中本地运行, 其他浏览器也将很快跟进.
创建一个微小的延迟加载路由器
考虑到这个理论, 让我们来制作我们的延迟加载路由器. 在应用程序模板中, 创建一些锚标记, 其中 href 是该页面组件模块的路径. 侦听这些元素上的单击事件, 防止重定向, 而是触发方法导航.
index.html
- <div id="app">
- <nav>
- <a href="/pages/BooksPage.js" @click.prevent="navigate">Books</a>
- <a href="/pages/MoviesPage.js" @click.prevent="navigate">Movies</a>
- <a href="/pages/GamesPage.js" @click.prevent="navigate">Games</a>
- </nav>
- <!--Where the page displays-->
- <component :is="page"></component>
- </div>
我们将在 Vue 实例中定义导航方法, 它将接受 click 事件作为参数. 使用链接的 href 值, 即 event.target. 动态导入所需的页面组件模块并将其分配给本地页面状态属性. 这是动态绑定到组件组件的.
App.JS
- import BooksPage from './pages/BooksPage.js';
- new Vue({
- el: '#app',
- data: {
- page: BooksPage
- },
- methods: {
- navigate(event) {
- this.page = () => import(`./${event.target.pathname}`)
- // Vue.JS <2.5.0
- // .then(m => m.default)
- ;
- }
- }
- });
注意, 2.5.0 之前的 Vue.JS 版本需要包含一个 then 回调函数来正确解析模块定义.
就是这样! 如果你在一个支持动态导入的浏览器中运行它, 你会看到:
回退
没有最新版本 Safari 或 Chrome 的用户怎么办? 他们需要一个后援. 让我们用 Webpack 创建一个.
首先, 我们必须稍微修改一下导航方法. Webpack 的 import() 实现要求它提前知道可能需要动态加载哪些文件. 您不需要指定确切的模块名称, 只需确保为 import() 提供的值是一个可解析的文件或目录.
为此, 我们将更改动态文件名, 以便它指定 pages 目录并从 href 中提取文件名."./ 页面 / $ {event.target.pathname.split ("/") .pop ()}". 在编译时, Webpack 足够聪明, 知道这意味着 "页面目录中的某些文件", 并将处理此目录中的任何 JavaScript 文件.
其次, 我们需要在函数中放入 comment /* webpackChunkName:"pages/[request]"*/, 以便 Webpack 知道将这个块提取到单独的文件中. 否则, Webpack 将把所有的页面组件模块打包在一个文件中, 延迟加载的好处就会丧失.
App.JS
- navigate(event) {
- this.page = () => import(
- /* webpackChunkName: "pages/[request]" */
- `./pages/${event.target.pathname.split('/').pop()}`
- )
- }
Webpack 配置
您可以使用这个简单的 Webpack 配置, 它有两个值得注意的特性:
指定一个 chunkFilename 输出属性. 这将确保在 Webpack 输出中正确地命名页面组件模块.
用 Babel 编译 JavaScript. 您将需要 Babel 的插件语法 - dynamic-import 来识别动态导入语句.
- var path = require('path');
- var webpack = require('webpack');
- module.exports = {
- entry: './app.js',
- output: {
- path: path.resolve(__dirname, './dist'),
- publicPath: '/dist/',
- filename: 'build.js',
- chunkFilename: '[name].js'
- },
- module: {
- rules: [
- {
- test: /\.JS$/,
- loader: 'babel-loader',
- exclude: /node_modules/,
- options: {
- plugins: [require('babel-plugin-syntax-dynamic-import')]
- }
- }
- ]
- }
- };
使用该配置运行 Webpack, 您将得到构建文件, 包括:
所有 JavaScript 模块文件都转换为 CommonJS 模块.
主包将包括 Webpack 的动态导入实现.
检查动态导入支持
如何告诉浏览器使用回退? 据我所知, 没有特定的方法来检查对 import() 的浏览器支持. 我的策略是在文档主体中使用内联脚本, 它创建一个新的脚本标记, 并根据对 import() 的支持有条件地更改源 (主脚本或回退).
- <script type="text/javascript">
- var script = document.createElement('script');
- try {
- Function('import("")');
- script.src = "app.js";
- script.type = 'module';
- } catch(e) {
- script.src = "dist/build.js";
- script.type = 'text/javascript';
- }
- document.body.appendChild(script);
- </script>
注意, 回退脚本被视为普通的 JavaScript 脚本, 而主脚本则被视为模块.
结论
如果我们可以使用 JavaScript 模块导入的本地实现, 就像延迟加载一样, 可以用更小的文件大小和更简单的实现来实现优化, 那就太好了. 例如, Webpack 的 import() 实现要求它提前知道可能需要动态加载哪些文件, 而本机实现则不需要.
在实践中, 这样的功能需要逐步增强, 因此需要 Webpack 作为后备, 重新引入相同的复杂性.
来源: http://www.css88.com/web/vue-js/12260.html