A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小刀葛小伦 于 2019-12-26 17:13 编辑

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

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




[JavaScript] 纯文本查看 复制代码
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组件配置



[JavaScript] 纯文本查看 复制代码
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



[JavaScript] 纯文本查看 复制代码
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



[JavaScript] 纯文本查看 复制代码
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 代码,并返回值




[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



[JavaScript] 纯文本查看 复制代码
<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



[JavaScript] 纯文本查看 复制代码
<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>

客户端渲染



[JavaScript] 纯文本查看 复制代码
// temolate<remote text='123456' @handleClick='handleClick'/>// script methods:{  handleClick(v){     console.log(v) // 点我  }}

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马