加载 vue 远程代码的组件实例详解


Posted in Javascript onNovember 20, 2017

在我们的 vue 项目中(特别是后台系统),总会出现一些需要多业务线共同开发同一个项目的场景,如果各业务团队向框架中提供一些私有的展示组件,但是这些组件并不能和框架一起打包,因为框架不能因为某个私有模块的频繁变更而重复构建发布。在这种场景下我们需要一个加载远程异步代码的组件来完成将这些组件加载到框架中。

vue-cli 作为 Vue 官方推荐的项目构建脚手架,它提供了开发过程中常用的,热重载,构建,调试,单元测试,代码检测等功能。我们本次的异步远端组件将基于 vue-cli 开发。

需求分析

  1. 如何加载远端的代码?
  2. 如何注册加载后的代码到框架中。
  3. 父组件如何和远端引入的组件通信。
  4. 远端代码如何复用框架中已引入的库。
  5. 避免因远端代码被类似 v-for 多次调用导致的不必要请求。

加载远端代码

远端代码应该存储在一个可访问的 URL 上,这样我们通过 Axios 类似的 HTTP client 请求这个链接拿到源码。

import Axios from 'axios';
export default {
 name: 'SyncComponent',
 props: {
  // 父组件提供请求地址
  url: {
   type: String,
   default: ''
  }
 },
 data() {
  return {
   resData: ''
  };
 },
 async mounted() {
  if (!this.url) return;
  const res = await Axios.get(this.url); // 我们在组件挂载完成时,请求远端代码并存储结果。
  this.resData = res.data;
 }
};

以上是基础代码 为了方便 一下例子中 我将省略重复的代码部分。

注册代码到框架中

这部分有些繁琐,涉及到多个问题:

浏览器并不支持 .vue 模板 或 ES.next 语法,模块需要编译后才可以使用。

处理这部分比较简单,我们自己定义一个webpack配置文件来打包这些模板。

// 在 build 目录下新建 webpack.sync-components.prod.conf.js 文件
const webpack = require('webpack');
const path = require('path');
const utils = require('./utils');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
function resolve(dir) {
 return path.join(__dirname, '..', dir)
}
module.exports = {
 // 此处引入要打包的组件
 entry: {
  componentA: resolve('/src/views/component-a.vue')
 },
 // 输出到静态目录下
 output: {
  path: resolve('/static/'),
  filename: '[name].js',
 },
 resolve: {
  extensions: ['.js', '.vue', '.json'],
  alias: {
   'vue$': 'vue/dist/vue.esm.js',
   '@': resolve('src'),
  }
 },
 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"'
  }),
  // 压缩JS
  new webpack.optimize.UglifyJsPlugin({
   compress: false,
   sourceMap: true
  }),
  // 压缩CSS 注意不做提取
  new OptimizeCSSPlugin({
   cssProcessorOptions: {
    safe: true
   }
  })
 ]
};

至此我们的模块已经被编译成框架可以识别的文件。

1.如何将字符串转换成js对象。

new Function。
async mounted() {
 if (!this.url) return;
 const res = await Axios.get(this.url);
 let Fn = Function;
 this.mode = new Fn(`return ${res.data}`)();
}

1.转换后的js对象并不能被vue识别。

有两种可能会导致这个问题:

// vue-loader v13 esModule 更新 默认值为 true, v12及之前版本为 false, 此项配置影响 vue 自身异步组件写法以及 webpack 打包结果
{
 test: /\.vue$/,
 loader: 'vue-loader',
 options: {
  esModule: false
  ... 以下省略千军万码
 }
}
// UglifyJs 需要取消变量名替换配置,此配置并不会极大影响压缩率
new webpack.optimize.UglifyJsPlugin({
 compress: false,
 sourceMap: true
})

至此 远程组件就被引入到框架中了。

父组件如何和远端引入的组件通信

这里有一个问题,从 view组件 到 远程异步加载组件 再到 实际业务组件 通信一共三层,中间层 远程异步组件 作为公共组件不可被修改,需要 view组件 直接向 实际业务组件 通信。vuex 和 eventBus 方案都过于繁琐,这里我们采用 $attrs 和 $listeners(vue v2.4+), 来实现 “fallthrough”(vue组件跨层级通信)。

// 修改 sync-component.vue 组件
// 新增 v-bind="$attrs" v-on="$listeners"
<component
 :is="mode"
 v-bind="$attrs"
 v-on="$listeners">
</component>
// inheritAttrs: true
export default {
 name: 'SyncComponent',
 props: {
  // 父组件提供请求地址
  url: {
   type: String,
   default: ''
  }
 },
 inheritAttrs: true
 ... 以下省略千军万码
}

远端代码如何复用框架中已引入的库

我们不希望看到远端组件和框架中存在较大库或插件的重复的引入,这部分内容尚处在实践阶段,主要思路是把公共库挂载到Vue原型链上实现组件公共复用 Vue.prototype.$xxx。

// 全局添加 axios 对象
import axios from 'axios';
Vue.prototype.$http = axios;

引入的远程组件可以访问到框架中的公共包了,这时候还需要配置 webpack 使远程组件打包时不要包含公共包的代码。

// webpack.sync-components.prod.conf.js 添加
externals: {
 vue: 'vue',
 'element-ui': 'element-ui',
 axios: 'axios'
}

避免因远端代码被类似 v-for 多次调用导致的不必要请求。

这部分我们直接用一个全局变量做字典,存储 以 请求地址:数据 为子项的数组。

async mounted() {
 if (!this.url) return;
 // Cache 缓存 根据 url 参数
 if (!window.SyncComponentCache) {
  window.SyncComponentCache = {};
 }
 let res;
 if (!window.SyncComponentCache[this.url]) {
  window.SyncComponentCache[this.url] = Axios.get(this.url);
  res = await window.SyncComponentCache[this.url];
 } else {
  res = await window.SyncComponentCache[this.url];
 }
 let Fn = Function;
 this.mode = new Fn(`return ${res.data}`)();
 console.log(this.mode);
}

至此,异步远程组件就可以加载并和框架进行通信了。

本文中的源码请访问 github 获取,组件已经发布到NPM 上,可以直接安装。

总结

以上所述是小编给大家介绍的加载 vue 远程代码的组件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
[原创]提供复制本站内容时出现,该文章转自脚本之家等字样的js代码
Mar 27 Javascript
JS点击链接后慢慢展开隐藏着图片的方法
Feb 17 Javascript
jQuery position() 函数详解以及jQuery中position函数的应用
Dec 14 Javascript
每日十条JavaScript经验技巧(二)
Jun 23 Javascript
javascript与jquery动态创建html元素示例
Jul 25 Javascript
jquery datatable服务端分页
Aug 31 Javascript
jQuery中绑定事件bind() on() live() one()的异同
Feb 23 Javascript
前端页面文件拖拽上传模块js代码示例
May 19 Javascript
详解使用angularjs的ng-options时如何设置默认值(初始值)
Jul 18 Javascript
mint-ui 时间插件使用及获取选择值的方法
Feb 09 Javascript
解决vue axios跨域 Request Method: OPTIONS问题(预检请求)
Aug 14 Javascript
VUE 项目在IE11白屏报错 SCRIPT1002: 语法错误的解决
Sep 27 Javascript
jquery中有哪些api jQuery主要API
Nov 20 #jQuery
详解如何将 Vue-cli 改造成支持多页面的 history 模式
Nov 20 #Javascript
详解Vue2 SSR 缓存 Api 数据
Nov 20 #Javascript
Three.js开发实现3D地图的实践过程总结
Nov 20 #Javascript
jquery ztree实现右键收藏功能
Nov 20 #jQuery
深入理解vuex2.0 之 modules
Nov 20 #Javascript
three.js中文文档学习之如何本地运行详解
Nov 20 #Javascript
You might like
php解压文件代码实现php在线解压
2014/02/13 PHP
THINKPHP内容分页代码分享
2015/01/14 PHP
在PHP站点的页面上添加Facebook评论插件的实例教程
2016/01/08 PHP
thinkphp3.2.3 分页代码分享
2016/07/28 PHP
php 二维数组快速排序算法的实现代码
2017/10/17 PHP
详解在YII2框架中使用UEditor编辑器发布文章
2018/11/02 PHP
基于php伪静态的实现方法解析
2020/07/31 PHP
JavaScript入门之基本函数详解
2011/10/21 Javascript
Js 回车换行处理的办法及replace方法应用
2013/01/24 Javascript
jquery和javascript的区别(常用方法比较)
2013/07/04 Javascript
JS获取键盘上任意按键的值(实例代码)
2013/11/12 Javascript
node.js中的fs.writeFile方法使用说明
2014/12/14 Javascript
jQuery实现DIV层收缩展开的方法
2015/02/27 Javascript
JavaScript每天定时更换皮肤样式的方法
2015/07/01 Javascript
基于JS实现的倒计时程序实例
2015/07/24 Javascript
Angular懒加载机制刷新后无法回退的快速解决方法
2016/08/30 Javascript
jQuery flip插件实现的翻牌效果示例【附demo源码下载】
2016/09/20 Javascript
在js代码拼接dom对象到页面上去的模板总结(必看)
2017/02/14 Javascript
详解webpack + vue + node 打造单页面(入门篇)
2017/09/23 Javascript
Vue2.0父子组件传递函数的教程详解
2017/10/16 Javascript
vue 自定义全局方法,在组件里面的使用介绍
2018/02/28 Javascript
微信小程序自定义组件封装及父子间组件传值的方法
2018/08/28 Javascript
vue移动端模态框(可传参)的实现
2019/11/20 Javascript
原生js实现轮播图特效
2020/05/04 Javascript
浅谈插入排序算法在Python程序中的实现及简单改进
2016/05/04 Python
Python编程scoketServer实现多线程同步实例代码
2018/01/29 Python
python实现学员管理系统
2019/02/26 Python
python 利用pywifi模块实现连接网络破解wifi密码实时监控网络
2019/09/16 Python
Pytorch通过保存为ONNX模型转TensorRT5的实现
2020/05/25 Python
python神经网络编程实现手写数字识别
2020/05/27 Python
维多利亚的秘密阿联酋官网:Victoria’s Secret阿联酋
2019/12/07 全球购物
财务人员担保书
2014/05/13 职场文书
搞笑的爱情检讨书
2014/10/01 职场文书
深入解析NumPy中的Broadcasting广播机制
2021/05/30 Python
Mysql使用全文索引(FullText index)的实例代码
2022/04/03 MySQL
SpringBoot整合Minio文件存储
2022/04/03 Java/Android