vue遠程加載sfc組件思路詳解
問題
在我們的 vue 項目中(特別是后臺系統(tǒng)),總會出現(xiàn)一些需要多業(yè)務(wù)線共同開發(fā)同一個項目的場景,如果各業(yè)務(wù)團隊向項目中提供一些公共業(yè)務(wù)組件,但是這些組件并不能和項目一起打包,因為項目中不能因為某個私有模塊的頻繁變更而重復(fù)構(gòu)建發(fā)布。
^_^不建議在生產(chǎn)環(huán)境使用,代碼包含eval
思路
在這種場景下我們需要將公共的業(yè)務(wù)組件部署到服務(wù)端,由客戶端請求并渲染組件。
服務(wù)端解析.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內(nèi)置,現(xiàn)在用來解析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()
// 標(biāo)簽添加data-u-id屬性
const template = sfc.template ? tagToUuid(sfc.template.content, componentId) : ''
// 轉(zhuǎn)化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 中的標(biāo)簽追加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 執(zhí)行其中的的 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>
遠程組件實踐
服務(wù)端sfc組件,注意javascript塊要使用module.exports導(dǎo)出,引入腳本使用$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) // 點我 }
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:js實現(xiàn)鼠標(biāo)點擊頁面彈出自定義文字效果
欄 目:JavaScript
本文標(biāo)題:vue遠程加載sfc組件思路詳解
本文地址:http://www.jygsgssxh.com/a1/JavaScript/9400.html
您可能感興趣的文章
- 01-10在Vue項目中使用Typescript的實現(xiàn)
- 01-10Vue中使用Lodop插件實現(xiàn)打印功能的簡單方法
- 01-10Vue filter 過濾當(dāng)前時間 實現(xiàn)實時更新效果
- 01-10Vuex實現(xiàn)數(shù)據(jù)共享的方法
- 01-10Vue+Node服務(wù)器查詢Mongo數(shù)據(jù)庫及頁面數(shù)據(jù)傳遞操作實例分析
- 01-10vue中根據(jù)時間戳判斷對應(yīng)的時間(今天 昨天 前天)
- 01-10Vue+Node實現(xiàn)的商城用戶管理功能示例
- 01-10vue實現(xiàn)拖拽效果
- 01-10vue圖片上傳組件使用詳解
- 01-10vue項目實現(xiàn)圖片上傳功能


閱讀排行
本欄相關(guān)
- 04-02javascript點線,點線的代碼
- 04-02javascript潛力,javascript強大嗎
- 04-02javascript替換字符串,js字符串的替換
- 04-02javascript移出,js 移入移出
- 04-02包含javascript舍的詞條
- 04-02javascript并行,深入理解并行編程 豆瓣
- 04-02javascript匿名,js匿名方法
- 04-02javascript警報,JavaScript警告
- 04-02javascript遮蓋,JavaScript遮蓋PC端頁面
- 04-02javascript前身,javascript的前身
隨機閱讀
- 04-02jquery與jsp,用jquery
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10C#中split用法實例總結(jié)
- 01-10delphi制作wav文件的方法
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 08-05DEDE織夢data目錄下的sessions文件夾有什


