加载 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 相关文章推荐
一个javascript参数的小问题
Mar 02 Javascript
js分解url参数(面向对象-极简主义法应用)
Aug 09 Javascript
javascript数字格式化通用类 accounting.js使用
Aug 24 Javascript
jQuery fadeTo方法调整图片的透明度使用介绍
May 06 Javascript
表格奇偶行设置不同颜色的核心JS代码
Dec 24 Javascript
Jquery选择器中使用变量实现动态选择例子
Jul 25 Javascript
js实现带圆角的两级导航菜单效果代码
Aug 24 Javascript
原生js的ajax和解决跨域的jsonp(实例讲解)
Oct 16 Javascript
在vue中使用css modules替代scroped的方法
Mar 10 Javascript
详解vue 数组和对象渲染问题
Sep 21 Javascript
如何用RxJS实现Redux Form
Dec 29 Javascript
你或许不知道的一些npm实用技巧
Jul 04 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
全国FM电台频率大全 - 15 山东省
2020/03/11 无线电
php empty,isset,is_null判断比较(差异与异同)
2010/10/19 PHP
PHP限制页面只能在微信自带浏览器访问的代码
2014/01/15 PHP
ThinkPHP之getField详解
2014/06/20 PHP
php过滤html标记属性类用法实例
2014/09/23 PHP
php使用pdo连接mssql server数据库实例
2014/12/25 PHP
Ajax请求PHP后台接口返回信息的实例代码
2018/08/21 PHP
JQUERY1.6 使用方法四 检测浏览器
2011/11/23 Javascript
js之ActiveX控件使用说明 new ActiveXObject()
2014/03/03 Javascript
jQuery插件zepto.js简单实现tab切换
2015/06/16 Javascript
JavaScript:Date类型全面解析
2016/05/19 Javascript
jQuery DateTimePicker 日期和时间插件示例
2017/01/22 Javascript
js阻止移动端页面滚动的两种方法
2017/01/25 Javascript
解决vuejs项目里css引用背景图片不能显示的问题
2018/09/13 Javascript
详解webpack4.x之搭建前端开发环境
2019/03/28 Javascript
小程序rich-text组件如何改变内部img图片样式的方法
2019/05/22 Javascript
jquery实现上传图片功能
2020/06/29 jQuery
使用scrapy实现爬网站例子和实现网络爬虫(蜘蛛)的步骤
2014/01/23 Python
Python使用 Beanstalkd 做异步任务处理的方法
2018/04/24 Python
Python实用技巧之列表、字典、集合中根据条件筛选数据详解
2018/07/11 Python
python 读写文件,按行修改文件的方法
2018/07/12 Python
使用TensorFlow直接获取处理MNIST数据方式
2020/02/10 Python
零基础小白多久能学会python
2020/06/22 Python
浅析python函数式编程
2020/09/26 Python
快时尚眼镜品牌,全国连锁眼镜店:LOHO眼镜生活
2018/10/08 全球购物
递归实现回文判断(如:abcdedbca就是回文,判断一个面试者对递归理解的简单程序)
2013/04/28 面试题
毕业自荐信
2013/12/16 职场文书
入党申请自荐书范文
2014/02/11 职场文书
捐款倡议书
2014/04/14 职场文书
知识改变命运演讲稿
2014/05/21 职场文书
优秀班组长事迹
2014/05/31 职场文书
捐书倡议书
2014/08/29 职场文书
环境建议书
2015/02/04 职场文书
市场部岗位职责范本
2015/04/15 职场文书
拯救大兵瑞恩观后感
2015/06/09 职场文书
原型和原型链 prototype和proto的区别详情
2021/11/02 Javascript