您好,登錄后才能下訂單哦!
這篇文章主要講解了“vue2從template到render模板編譯怎么實現”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“vue2從template到render模板編譯怎么實現”吧!
在vue的渲染過程中,渲染核心邏輯是vm._update(vm._render(), hydrating),通過vm._render的執行獲取到vNode,再通過vm._update的執行來將vNode渲染成真實視圖。
其中,render函數的來源是:
(1)用戶手寫;
(2)通過vue-loader引入的時候進行轉換;
(3)通過compileToFunctions將template進行處理產生。
開發過程中主要以template的方式進行代碼的編寫,這里主要梳理compileToFunctions的方法。
在src/platforms/web/entry-runtime-with-compiler.js中在vue的原型上定義了$mount:
const idToTemplate = cached(id => { const el = query(id) return el && el.innerHTML }) const mount = Vue.prototype.$mount Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && query(el) /* istanbul ignore if */ if (el === document.body || el === document.documentElement) { process.env.NODE_ENV !== 'production' && warn( `Do not mount Vue to <html> or <body> - mount to normal elements instead.` ) return this } const options = this.$options // resolve template/el and convert to render function if (!options.render) { let template = options.template if (template) { if (typeof template === 'string') { if (template.charAt(0) === '#') { template = idToTemplate(template) /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && !template) { warn( `Template element not found or is empty: ${options.template}`, this ) } } } else if (template.nodeType) { template = template.innerHTML } else { if (process.env.NODE_ENV !== 'production') { warn('invalid template option:' + template, this) } return this } } else if (el) { template = getOuterHTML(el) } if (template) { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { mark('compile') } const { render, staticRenderFns } = compileToFunctions(template, { outputSourceRange: process.env.NODE_ENV !== 'production', shouldDecodeNewlines, shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this) options.render = render options.staticRenderFns = staticRenderFns /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { mark('compile end') measure(`vue ${this._name} compile`, 'compile', 'compile end') } } } return mount.call(this, el, hydrating) } /** * Get outerHTML of elements, taking care * of SVG elements in IE as well. */ function getOuterHTML (el: Element): string { if (el.outerHTML) { return el.outerHTML } else { const container = document.createElement('div') container.appendChild(el.cloneNode(true)) return container.innerHTML } }
在沒有編寫render時候才會去獲取template并進行編譯,獲取方式有
在傳入的參數中獲取options.template
如果是真實節點則獲取其innerHTML
以上都不滿足則通過或者el.outerhTML的方式獲取
獲取到template后通過compileToFunctions的方式進行編譯,這里是編譯的入口。
在src/platforms/web/compile/index.js中調用了createCompiler:
import { baseOptions } from './options' import { createCompiler } from 'compiler/index' const { compile, compileToFunctions } = createCompiler(baseOptions) export { compile, compileToFunctions }
這里將baseOptions作為基礎參數傳入,在src/complile/index.js中定義了createCompiler:
import { parse } from './parser/index' import { optimize } from './optimizer' import { generate } from './codegen/index' import { createCompilerCreator } from './create-compiler' export const createCompiler = createCompilerCreator(function baseCompile ( template: string, options: CompilerOptions ): CompiledResult { const ast = parse(template.trim(), options) if (options.optimize !== false) { optimize(ast, options) } const code = generate(ast, options) return { ast, render: code.render, staticRenderFns: code.staticRenderFns } })
這里定義了baseCompile核心參數,主要目的是進行ast的獲取、ast的優化和code的拼接。并將baseCompile作為參數傳入執行了createCompilerCreator:
export function createCompilerCreator (baseCompile: Function): Function { return function createCompiler (baseOptions: CompilerOptions) { function compile ( template: string, options?: CompilerOptions ): CompiledResult { // ... } return { compile, compileToFunctions: createCompileToFunctionFn(compile) } } }
createCompiler就是createCompilerCreator的返回函數,createCompiler中返回了compile和compileToFunctions,這里的compileToFunctions就是入口獲取render的函數,由createCompileToFunctionFn(compile)執行獲得。
再看createCompileToFunctionFn(compile):
function createFunction (code, errors) { try { return new Function(code) } catch (err) { errors.push({ err, code }) return noop } } export function createCompileToFunctionFn (compile: Function): Function { const cache = Object.create(null) return function compileToFunctions ( template: string, options?: CompilerOptions, vm?: Component ): CompiledFunctionResult { options = extend({}, options) const warn = options.warn || baseWarn delete options.warn /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production') { // detect possible CSP restriction try { new Function('return 1') } catch (e) { if (e.toString().match(/unsafe-eval|CSP/)) { warn( 'It seems you are using the standalone build of Vue.js in an ' + 'environment with Content Security Policy that prohibits unsafe-eval. ' + 'The template compiler cannot work in this environment. Consider ' + 'relaxing the policy to allow unsafe-eval or pre-compiling your ' + 'templates into render functions.' ) } } } // check cache const key = options.delimiters ? String(options.delimiters) + template : template if (cache[key]) { return cache[key] } // compile const compiled = compile(template, options) // ... // turn code into functions const res = {} const fnGenErrors = [] res.render = createFunction(compiled.render, fnGenErrors) res.staticRenderFns = compiled.staticRenderFns.map(code => { return createFunction(code, fnGenErrors) }) // check function generation errors. // this should only happen if there is a bug in the compiler itself. // mostly for codegen development use /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production') { if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { warn( `Failed to generate render function:\n\n` + fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n$[code]\n`).join('\n'), vm ) } } return (cache[key] = res) } }
這里通過const cache = Object.create(null)的方式定義了緩存,返回的compileToFunctions函數中執行return (cache[key] = res),通過閉包的方式進行了計算的重復利用。
如果當前環境支持new Function('return 1')則調用了createFunction將compiled.render通過new Function(code)進行可執行代碼的轉換,否則進行提示(放寬環境執行或預編譯當前模板)。
再看const compiled = compile(template, options):
function compile ( template: string, options?: CompilerOptions ): CompiledResult { const finalOptions = Object.create(baseOptions) const errors = [] const tips = [] let warn = (msg, range, tip) => { (tip ? tips : errors).push(msg) } if (options) { if (process.env.NODE_ENV !== 'production' && options.outputSourceRange) { // $flow-disable-line const leadingSpaceLength = template.match(/^\s*/)[0].length warn = (msg, range, tip) => { const data: WarningMessage = { msg } if (range) { if (range.start != null) { data.start = range.start + leadingSpaceLength } if (range.end != null) { data.end = range.end + leadingSpaceLength } } (tip ? tips : errors).push(data) } } // merge custom modules if (options.modules) { finalOptions.modules = (baseOptions.modules || []).concat(options.modules) } // merge custom directives if (options.directives) { finalOptions.directives = extend( Object.create(baseOptions.directives || null), options.directives ) } // copy other options for (const key in options) { if (key !== 'modules' && key !== 'directives') { finalOptions[key] = options[key] } } } finalOptions.warn = warn const compiled = baseCompile(template.trim(), finalOptions) if (process.env.NODE_ENV !== 'production') { detectErrors(compiled.ast, warn) } compiled.errors = errors compiled.tips = tips return compiled }
這里的參數就是入口處獲取到的template,options就是入口處傳入的用戶參數
{ outputSourceRange: process.env.NODE_ENV !== 'production', shouldDecodeNewlines, shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }
然后進行平臺參數和用戶參數的合并。最后將合并后的參數傳入并執行
const compiled = baseCompile(template.trim(), finalOptions)
感謝各位的閱讀,以上就是“vue2從template到render模板編譯怎么實現”的內容了,經過本文的學習后,相信大家對vue2從template到render模板編譯怎么實現這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。