vue远程加载sfc组件思路详解

问题

在我们的 vue 项目中(特别是后台系统),总会出现一些需要多业务线共同开发同一个项目的场景,如果各业务团队向项目中提供一些公共业务组件,但是这些组件并不能和项目一起打包,因为项目中不能因为某个私有模块的频繁变更而重复构建发布。

^_^不建议在生产环境使用,代码包含eval

思路

在这种场景下我们需要将公共的业务组件部署到服务端,由客户端请求并渲染组件。

服务端解析.vue文件

使用vue-template-compiler 模板解析器,解析SFC(单文件组件)

const compile = require('vue-template-compiler')

// 获取sfc组件的源码
const str = fs.readFileSync(path.resolve(__dirname, `../components/sfc.vue`), 'utf-8')

// vue-loader内置,现在用来解析SFC(单文件组件)
let sfc = compile.parseComponent(str)

// 获取sfc组件配置
let sfcOptions = getComponentOption(sfc)

getComponentOption 获取sfc组件配置

import { uuid } from 'utilscore'
import stylus from 'stylus'
import sass from 'sass'
import less from 'less'
const getComponentOption = sfc => {
  // 生成data-u-id
  const componentId = uuid(8, 16).toLocaleLowerCase()
  // 标签添加data-u-id属性
  const template = sfc.template ? tagToUuid(sfc.template.content, componentId) : ''
  // 转化style(less、sass、stylus)
  let styles = []
  sfc.styles.forEach(sty => {
    switch (sty.lang) {
      case 'stylus':
        stylus.render(sty.content, (err, css) => styles.push(formatStyl(sty, css, componentId)))
        break;
      case 'sass':
      case 'scss':
        styles.push(formatStyl(sty, sass.renderSync({ data: sty.content }).css.toString(), componentId))
        break;
      case 'less':
        less.render(sty.content, (err, css) => styles.push(formatStyl(sty, css, componentId)))
        break;
    }
  })
  let options = {
    script: sfc.script ? $require(null, sfc.script.content) : {},
    styles,
    template
  }
  return JSON.stringify(options, (k, v) => {
    if(typeof(v) === 'function') {
      let _fn = v.toString()
      return /^function()/.test(_fn) ? _fn : fn.replace(/^/,'function ')
    }
    return v
  })
}

tagToUuid  给template 中的标签追加data-u-id

const tagToUuid = (tpl, id) => {
  var pattern = /<[^\/]("[^"]*"|'[^']*'|[^'">])*>/g
  return tpl.replace(pattern, $1 => {
    return $1.replace(/<([\w\-]+)/i, ($2, $3) => `<${$3} data-u-${id}`)
  })
}

formatStyl 处理样式的scoped

const formatStyl = (sty, css, componentId) => {
  let cssText = css
  if (sty.scoped) {
    cssText = css.replace(/[\.\w\>\s]+{/g, $1 => {
    if (/>>>/.test($1)) return $1.replace(/\s+>>>/, `[data-u-${componentId}]`)
    return $1.replace(/\s+{/g, $2 => `[data-u-${componentId}]${$2}`)
    })
  }
  return cssText
}

$require 执行其中的的 JavaScript 代码,并返回值

const $require = (filepath, scriptContext) => {
  const filename = path.resolve(__dirname, `../${filepath}`);
  const module = { exports: {} }
  let code = scriptContext ? scriptContext : fs.readFileSync(filename, 'utf-8')
  let exports = module.exports
  code = `(function($require,module,exports,__dirname,filename){$[code]})($require,module,exports,__dirname,filename)`
  eval(code)
  return module.exports
} 

客户端请求组件并渲染

封装前端远程组件-remote.vue

<template>
  <component :is="remote" v-bind="$attrs" v-on="$listeners"></component>
</template>
<script>
import Vue from "vue";
export default {
  data() {
    return {
      remote: null
    }
  },
  props: {
    tagName: {
      type: String,
      defualt: "componentName"
    }
  },
  created() {
    fetch("http://localhost:3000/getComponent/"+this.tagName)
      .then(res => res.json())
      .then(sfc => {
        let options = this.parseObj(sfc);
        options.styles.forEach(css => this.appendSty(css));
        this.remote = Vue.extend({
          ...options.script,
          name: options.script.name || this.tagName,
          template: options.template
        });
      });
   },
   methods: {
    isObject(v) {
      return Object.prototype.toString.call(v).includes("Object");
    },
    parseObj(data) {
      if (Array.isArray(data)) return data.map(row => this.parseObj(row));
      if (this.isObject(data)) {
        let ret = {};
        for (let k in data) {
          ret[k] = this.parseObj(data[k]);
         }    return ret;
      }
      try {
        let pattern = /function ([\w]+)\(\) \{ \[native code\] \}/;
        if (pattern.test(data)) {
          return window[pattern.exec(data)[1]];
        } else {
          let evalData = eval(`(${data})`);
          return typeof evalData == "function" ? evalData : data;
        }
      } catch (err) {
        return data;
      }
    },
    appendSty(css) { // 生成组件样式
      let style = document.createElement("style");
      style.setAttribute("type", "text/css");
      var cssText = document.createTextNode(css);
      style.appendChild(cssText);
      var head = document.querySelector("head");
      head.appendChild(style);
    }
}};
</script>

远程组件实践

服务端sfc组件,注意javascript块要使用module.exports导出,引入脚本使用$require

<template>
  <div class="test">
    <div>
      <p @click='$emit("handleClick",'点我')'>远程组件--{{msg}}--{{text}}</p>
      </div>
    </div>
</template>
<script>
// 加载js脚本
let {a} = $require('utils/test.js')
module.exports = {
  data: function() {
    return {
      msg: "remote component",
      ...a,
    }
  },
  props: {
    text: {
      type: Boolean,
      default: true
    }
  },
  mounted:function(){
    console.log('prop text is',this.text)
  }
};
</script>
<style lang="stylus" scoped>
.test {
  .test2 {
     color: red;
  }
  p{
     color:red
  }
}
</style>

客户端渲染

// temolate
<remote text='123456' @handleClick='handleClick'/>

// script
methods:{
 handleClick(v){
   console.log(v) // 点我 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

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

    在我们的 vue 项目中(特别是后台系统),总会出现一些需要多业务线共同开发同一个项目的场景,如果各业务团队向框架中提供一些私有的展示组件,但是这些组件并不能和框架一起打包,因为框架不能因为某个私有模块的频繁变更而重复构建发布.在这种场景下我们需要一个加载远程异步代码的组件来完成将这些组件加载到框架中. vue-cli 作为 Vue 官方推荐的项目构建脚手架,它提供了开发过程中常用的,热重载,构建,调试,单元测试,代码检测等功能.我们本次的异步远端组件将基于 vue-cli 开发. 需求分析 如

  • vue远程加载sfc组件思路详解

    问题 在我们的 vue 项目中(特别是后台系统),总会出现一些需要多业务线共同开发同一个项目的场景,如果各业务团队向项目中提供一些公共业务组件,但是这些组件并不能和项目一起打包,因为项目中不能因为某个私有模块的频繁变更而重复构建发布. ^_^不建议在生产环境使用,代码包含eval 思路 在这种场景下我们需要将公共的业务组件部署到服务端,由客户端请求并渲染组件. 服务端解析.vue文件 使用vue-template-compiler 模板解析器,解析SFC(单文件组件) const compile

  • vue-cli项目中使用公用的提示弹层tips或加载loading组件实例详解

    项目结构,在组件文件夹(components)下新建common文件夹,所用公用组件放里面,本例包含tips和loading两个 一.loading组件 1.loading.vue组件内容如下: 代码: <template> <div class="loading" v-show="loading"> <img src="./loading.gif"> </div> </template>

  • vue3如何按需加载第三方组件库详解

    前言 以Element Plus为例,配置按需加载组件和样式. 环境 vue3.0.5 vite2.3.3 安装 Element Plus yarn add element-plus # OR npm install element-plus --save 完整引入 import { createApp } from 'vue' import ElementPlus from 'element-plus'; import 'element-plus/lib/theme-chalk/index.c

  • 使用Vue制作图片轮播组件思路详解

    之前一直都没有认真的写过一个组件.以前在写业务代码的过程中,都是用的别人封装好的组件,这次尝试着写了一个图片轮播组件,虽然比不上知名的轮播组件,但它的功能基本完整,而且在写这个组件的过程中,学的东西也很多,在这里也给大家分享出来,如有疏漏,欢迎指正! 在制作这个组件之前,笔者google了不少关于轮播的文章,发现实现一个轮播的思路虽然各有不同,但是大的逻辑其实差不多,本文主要依据慕课网上焦点轮播图特效这节课,不过慕课网主要用原生JS写,而笔者则用Vue进行了重构,并且进行了一点修改.完成后的组件

  • vue 项目常用加载器及配置详解

    本文介绍了vue 项目常用加载器及配置详解,分享给大家,具体如下: 1.安装sass: 1.1 由于sass-loader依赖于node-sass,所以在安装sass-loader的同时还需安装node-sass npm install --save-dev node-sass npm install --save-dev sass-loader 1.2 安装完成后修改 <style>标签: <style lang="scss"></style> 2

  • 基于Vue渲染与插件的加载顺序的问题详解

    Vue实践分享(三)在实际项目的开发过程中,经常会遇到页面还没渲染完成而插件就已经开始加载的问题,这样就会导致显示和功能出错. 可以通过Vue中的nextTick来解决 Vue.nextTick(function() { //widget }); 这样就会在页面渲染完成后再执行nextTick内的插件 以上这篇基于Vue渲染与插件的加载顺序的问题详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们. 您可能感兴趣的文章: 浅谈Vue的加载顺序探讨 Vue.js学习教程

  • Vue首页界面加载优化实现方法详解

    目录 1.路由懒加载 2.js 资源异步加载 3.图片懒加载 4.组件分包懒加载-在视口才加载 1.路由懒加载 问题: 项目在打包时会将首页与其他页面的资源打包到同一个资源文件,造成首页加载的资源文件过大. 解决方法: 路由懒加载:打包时会将每个路由页面拆分成单独的 js 资源,同时跳转到对应页面才会加载对应路由的 js 资源. { path: "/about", name: "about", component: () => import(/* webpac

  • Vue异步加载about组件

    本文实例为大家分享了Vue异步加载about组件的具体代码,供大家参考,具体内容如下 异步加载about组件 about.js Vue.component('about', {template: '<div>ABOUT PAGE</div>'}); html代码: <div id="app"> <router-link to="/home">/home</router-link> <router-li

  • Vue动态加载异步组件的方法

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

  • Vue项目全局配置微信分享思路详解

    这个项目为移动端项目,主要用于接入公众号服务.项目采用两种登录方式,微信授权登录以及账号密码登录.对于移动端项目而言,为了便于项目扩展以及提供开发热更新速度,项目分为不同的模块,每个模块是一个单页面应用.页面分为两种,一种是需要用户登录之后才能浏览,另一种是用户无需登录即可浏览.无论哪一种,均配置微信分享. 使用的技术 1.使用vue作为框架 2.使用vux作为UI组件库 全局配置微信分享思路 1.区分一般和特殊,一般情况,全局配置默认分享文案:特殊情况分两种,一种是分享内容不需要异步获取,则在

随机推荐