温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么在Vue中动态加载异步组件

发布时间:2021-05-19 17:59:05 来源:亿速云 阅读:366 作者:Leah 栏目:web开发

今天就跟大家聊聊有关怎么在Vue中动态加载异步组件,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

背景:

目前我们项目都是按组件划分的,然后各个组件之间封装成产品。目前都是采用iframe直接嵌套页面。项目中我们还是会碰到一些通用的组件跟业务之间有通信,这种情况下iframe并不是最好的选择,iframe存在跨域的问题,当然是postMessage还是可以通信的,但也并非是最好的。目前有这么一个场景:门户需要制作通用的首页和数据概览页面,首页和数据概览页面通过小部件来自由拼接。业务组件在制作的时候只需要提供各个模块小部件的url就可以了,可是如果小部件之间还存在联系呢?那么iframe是不好的。目前采用Vue动态加载异步组件的方式来实现小组件之间的通信。当然门户也要提供一个通信的基线:Vue事件总线(空的Vue实例对象)。

内容:

使用过vue的都应该知道vue的动态加载组件components:

Vue通过is来绑定需要加载的组件。那么我们现在需要的就是如何打包组件,如果通过复制业务组件内部的代码,那么这种就需要把依赖全部找齐,并复制过去(很多情况下会漏下某个图片或css等),这种方式是比较low的,不方便维护。因此我们需要通过webpack来打包单个vue文件成js,这边一个vue打包成一个js,不需压代码分割,css分离。因为component加载时只需要加载一个文件即可。打包文件配置如下:

首先在package.json加入打包命令:

"scripts": {   ...   "build-outCMP": "node build/build-out-components.js"  },

Build-out-components.js文件:

'use strict' require('./check-versions')() process.env.NODE_ENV = 'production' const ora = require('ora') const path = require('path') const chalk = require('chalk') const webpack = require('webpack') const webpackConfig = require('./webpack.out-components.prod.conf') const spinner = ora('building for sync-components...') spinner.start() webpack(webpackConfig, function (err, stats) {  spinner.stop()  if (err) throw err  process.stdout.write(stats.toString({   colors: true,   modules: false,   children: false,   chunks: false,   chunkModules: false  }) + '\n\n')  if (stats.hasErrors()) {   console.log(chalk.red(' Build failed with errors.\n'))   process.exit(1)  }  console.log(chalk.cyan(' Build complete.\n'))  console.log(chalk.yellow(   ' Tip: built files are meant to be served over an HTTP server.\n' +   ' Opening index.html over file:// won\'t work.\n'  )) })

webpack.out-components.prod.conf.js文件配置如下

const webpack = require('webpack'); const path = require('path'); const utils = require('./utils'); const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') const {entry, mkdirsSync} = require('./out-components-tools') function resolve(dir) {  return path.join(__dirname, '..', dir) } mkdirsSync(resolve('/static/outComponents')) module.exports = {  entry: entry,  output: {   path: resolve('/static/outComponents'),   filename: '[name].js',  },  resolve: {   extensions: ['.js', '.vue', '.json'],   alias: {    'vue$': 'vue/dist/vue.esm.js',    '@': resolve('src'),   }  },  externals: {   vue: 'vue',   axios: 'axios'  },  module: {   rules: [    {     test: /\.vue$/,     loader: 'vue-loader',     options: {      esModule: false, // vue-loader v13 更新 默认值为 true v12及之前版本为 false, 此项配置影响 vue 自身异步组件写法以及 webpack 打包结果      loaders: utils.cssLoaders({       sourceMap: true,       extract: false     // css 不做提取      }),      transformToRequire: {       video: 'src',       source: 'src',       img: 'src',       image: 'xlink:href'      }     }    },    {     test: /\.js$/,     loader: 'babel-loader',     include: [resolve('src'), resolve('test')]    },    {     test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,     loader: 'url-loader',     options: {      limit: 10000,      name: utils.assetsPath('img/[name].[hash:7].[ext]')     }    },    {     test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,     loader: 'url-loader',     options: {      limit: 10000,      name: utils.assetsPath('media/[name].[hash:7].[ext]')     }    },    {     test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,     loader: 'url-loader',     options: {      limit: 10000,      name: utils.assetsPath('fonts/[name].[hash:7].[ext]')     }    }   ]  },  plugins: [   new webpack.DefinePlugin({    'process.env.NODE_ENV': '"production"'   }),   // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify   new webpack.optimize.UglifyJsPlugin({    compress: false,    sourceMap: true   }),   // Compress extracted CSS. We are using this plugin so that possible   // duplicated CSS from different components can be deduped.   new OptimizeCSSPlugin({    cssProcessorOptions: {     safe: true    }   })  ] };

out-components-tools.js文件配置如下:

const glob = require('glob') const fs = require('fs'); const path = require('path'); // 遍历要打包的组件 let entry = {} var moduleSrcArray = glob.sync('./src/out-components/*') for(var x in moduleSrcArray){  let fileName = (moduleSrcArray[x].split('/')[3]).slice(0, -4)  entry[fileName] = moduleSrcArray[x] } // 清理文件 function mkdirsSync(dirname) {  if (fs.existsSync(dirname)) {   deleteall(dirname)   return true;  } else {   if (mkdirsSync(path.dirname(dirname))) {    fs.mkdirSync(dirname);    return true;   }  } } // 删除文件下的文件 function deleteall(path) {  var files = [];  if(fs.existsSync(path)) {   files = fs.readdirSync(path);   files.forEach(function(file, index) {    var curPath = path + "/" + file;    if(fs.statSync(curPath).isDirectory()) { // recurse     deleteall(curPath);    } else { // delete file     fs.unlinkSync(curPath);    }   });  } }; exports.entry = entry exports.mkdirsSync = mkdirsSync

build-out-components是打包的入口文件,webpack.out-components.prod.conf.js是webpack打包的配置文件,out-components-tools.js是工具库,这边是打包的entry自动获取(默认为src/out-components),还有自动删除之前打包的文件。

目前的文件目录为

怎么在Vue中动态加载异步组件

通过打包生产文件:

怎么在Vue中动态加载异步组件

在static下outComponents文件夹内的js文件。(最终打包需要打包到dist下面,这边做测试先打包在static文件下,方便后续动态组件ajax获取组件使用)

门户的小部件是通过配置url,和调整布局来生产的。因此业务组件至此已经完成了。只需要提供对门户暴露的url即可。
接下来就是门户这边加载动态组件的实现了。门户这边就相对简单了。看如下图配置:

门户通过component的动态组件来实现加载异步组件,通过ajax请求刚才打包的url,然后实例化函数new Function来赋值给mode(new Function之所以分成2部,是因此效验规则的问题,可忽略)。这样就实现了动态加载异步组件了。门户和业务组件可以各个开发,任何业务开发数据概览,门户都不需要改代码,只需要界面上配置url即可。这个异步加载组件已经结束了。这边门户需要封装一封实现异步组件。父级只需要传入url即可。这边还有个可以优化的是,可以把mode优先缓存,那么不需要每次都去加载请求。如下:

我们可以看到在门户的一个数据概览页面上加载了多个异步组件,那么异步组件之间也是可能存在通信的,这样该如何做呢?因为现在已经不是iframe嵌套了,可以通过监听一个组件,然调用另一个组件的方法,这样确实可以实现平级组件间的通信,但这样势必不可取的,因为一旦这样做了门户必须要根据业务来辅助,修改代码来实现功能。因此这边借用门户来生成vue事件总线(空的vue实例)来实现。

门户代码如下: 在this.$root上挂在一个事件总线:

 created () {     if (!this.$root.eventBus) {      this.$root.eventBus = new Vue()     }    }

然后业务组件之间就可以根据自己的业务实现通信:

组件一和组件二代码如下:

 <template>    <div class="test1">     这是一个外部组件a1     <hello-word></hello-word>    </div>     </template>      <script>   import helloWord from '../components/HelloWorld'   export default {    data () {     return {      i: 0     }    },    components: {     helloWord    },    mounted () {     setInterval(() => {      this.i++      if (this.i < 10) {       this.test()      }     }, 1000)    },    methods: {     test () {      this.$root.eventBus.$emit('childEvent', this.i)     }    }   }   </script>
 <template>    <div class="test1">     这也是外部组件哦     <div >      这是a1传来的{{a1}}     </div>    </div>   </template>      <script>   export default {    data () {     return {      a1: 0     }    },    created () {     this.$root.eventBus.$on('childEvent', this.change)    },    methods: {     change (i) {      this.a1 = i     }    }   }   </script>

为什么要使用Vue

Vue是一款友好的、多用途且高性能的JavaScript框架,使用vue可以创建可维护性和可测试性更强的代码库,Vue允许可以将一个网页分割成可复用的组件,每个组件都包含属于自己的HTML、CSS、JavaScript,以用来渲染网页中相应的地方,所以越来越多的前端开发者使用vue。

看完上述内容,你们对怎么在Vue中动态加载异步组件有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

vue
AI