mirror of
https://gitee.com/vuejs/vue.git
synced 2024-12-02 12:07:39 +08:00
introduce tip + make v-for component key warning a tip + refactor web compiler entry
This commit is contained in:
parent
7d3309deed
commit
f66028b9cd
@ -24,6 +24,7 @@ declare type CompiledResult = {
|
||||
render: string;
|
||||
staticRenderFns: Array<string>;
|
||||
errors?: Array<string>;
|
||||
tips?: Array<string>;
|
||||
}
|
||||
|
||||
declare type CompiledFunctionResult = {
|
||||
|
@ -152,7 +152,8 @@ function genFor (el: any): string {
|
||||
warn(
|
||||
`<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` +
|
||||
`v-for should have explicit keys. ` +
|
||||
`See https://vuejs.org/guide/list.html#key for more info.`
|
||||
`See https://vuejs.org/guide/list.html#key for more info.`,
|
||||
true /* tip */
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import config from '../config'
|
||||
import { noop } from 'shared/util'
|
||||
|
||||
let warn = noop
|
||||
let tip = noop
|
||||
let formatComponentName
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
@ -19,6 +20,14 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
}
|
||||
}
|
||||
|
||||
tip = (msg, vm) => {
|
||||
if (hasConsole && (!config.silent)) {
|
||||
console.warn(`[Vue tip]: ${msg} ` + (
|
||||
vm ? formatLocation(formatComponentName(vm)) : ''
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
formatComponentName = (vm, includeFile) => {
|
||||
if (vm.$root === vm) {
|
||||
return '<Root>'
|
||||
@ -47,4 +56,4 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
}
|
||||
}
|
||||
|
||||
export { warn, formatComponentName }
|
||||
export { warn, tip, formatComponentName }
|
||||
|
@ -62,7 +62,6 @@ Vue.prototype.$mount = function (
|
||||
}
|
||||
|
||||
const { render, staticRenderFns } = compileToFunctions(template, {
|
||||
warn: msg => warn(msg, this),
|
||||
shouldDecodeNewlines,
|
||||
delimiters: options.delimiters
|
||||
}, this)
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
|
||||
import { isUnaryTag } from './util'
|
||||
import { warn } from 'core/util/debug'
|
||||
import { warn, tip } from 'core/util/debug'
|
||||
import { detectErrors } from 'compiler/error-detector'
|
||||
import { compile as baseCompile } from 'compiler/index'
|
||||
import { extend, genStaticKeys, noop } from 'shared/util'
|
||||
@ -24,43 +24,43 @@ export const baseOptions: CompilerOptions = {
|
||||
isPreTag
|
||||
}
|
||||
|
||||
function compileWithOptions (
|
||||
template: string,
|
||||
options?: CompilerOptions
|
||||
): CompiledResult {
|
||||
options = options
|
||||
? extend(extend({}, baseOptions), options)
|
||||
: baseOptions
|
||||
return baseCompile(template, options)
|
||||
}
|
||||
|
||||
export function compile (
|
||||
template: string,
|
||||
options?: CompilerOptions
|
||||
): CompiledResult {
|
||||
options = options || {}
|
||||
const finalOptions = Object.create(baseOptions)
|
||||
const errors = []
|
||||
// allow injecting modules/directives
|
||||
const baseModules = baseOptions.modules || []
|
||||
const modules = options.modules
|
||||
? baseModules.concat(options.modules)
|
||||
: baseModules
|
||||
const directives = options.directives
|
||||
? extend(extend({}, baseOptions.directives), options.directives)
|
||||
: baseOptions.directives
|
||||
const compiled = compileWithOptions(template, {
|
||||
modules,
|
||||
directives,
|
||||
preserveWhitespace: options.preserveWhitespace,
|
||||
warn: msg => {
|
||||
errors.push(msg)
|
||||
}
|
||||
})
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
compiled.errors = errors.concat(detectErrors(compiled.ast))
|
||||
} else {
|
||||
compiled.errors = errors
|
||||
const tips = []
|
||||
finalOptions.warn = (msg, tip) => {
|
||||
(tip ? tips : errors).push(msg)
|
||||
}
|
||||
|
||||
if (options) {
|
||||
// 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),
|
||||
options.directives
|
||||
)
|
||||
}
|
||||
// copy other options
|
||||
for (const key in options) {
|
||||
if (key !== 'modules' && key !== 'directives') {
|
||||
finalOptions[key] = options[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const compiled = baseCompile(template, finalOptions)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
errors.push.apply(errors, detectErrors(compiled.ast))
|
||||
}
|
||||
compiled.errors = errors
|
||||
compiled.tips = tips
|
||||
return compiled
|
||||
}
|
||||
|
||||
@ -70,20 +70,15 @@ export function compileToFunctions (
|
||||
vm?: Component
|
||||
): CompiledFunctionResult {
|
||||
options = extend({}, options)
|
||||
const _warn = options.warn || warn
|
||||
const errors = []
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
options.warn = msg => {
|
||||
errors.push(msg)
|
||||
}
|
||||
|
||||
// detect possible CSP restriction
|
||||
try {
|
||||
new Function('return 1')
|
||||
} catch (e) {
|
||||
if (e.toString().match(/unsafe-eval|CSP/)) {
|
||||
_warn(
|
||||
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 ' +
|
||||
@ -93,41 +88,64 @@ export function compileToFunctions (
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check cache
|
||||
const key = options.delimiters
|
||||
? String(options.delimiters) + template
|
||||
: template
|
||||
if (cache[key]) {
|
||||
return cache[key]
|
||||
}
|
||||
|
||||
// compile
|
||||
const compiled = compile(template, options)
|
||||
|
||||
// check compilation errors/tips
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (compiled.errors && compiled.errors.length) {
|
||||
warn(
|
||||
`Error compiling template:\n\n${template}\n\n` +
|
||||
compiled.errors.map(e => `- ${e}`).join('\n') + '\n',
|
||||
vm
|
||||
)
|
||||
}
|
||||
if (compiled.tips && compiled.tips.length) {
|
||||
compiled.tips.forEach(msg => tip(msg, vm))
|
||||
}
|
||||
}
|
||||
|
||||
// turn code into functions
|
||||
const res = {}
|
||||
const compiled = compileWithOptions(template, options)
|
||||
res.render = makeFunction(compiled.render)
|
||||
const fnGenErrors = []
|
||||
res.render = makeFunction(compiled.render, fnGenErrors)
|
||||
const l = compiled.staticRenderFns.length
|
||||
res.staticRenderFns = new Array(l)
|
||||
for (let i = 0; i < l; i++) {
|
||||
res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i])
|
||||
res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], 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 (
|
||||
errors.length ||
|
||||
res.render === noop ||
|
||||
res.staticRenderFns.some(fn => fn === noop)
|
||||
) {
|
||||
const allErrors = errors.concat(detectErrors(compiled.ast))
|
||||
_warn(
|
||||
`Error compiling template:\n\n${template}\n\n` +
|
||||
(allErrors.length ? allErrors.map(e => `- ${e}`).join('\n') + '\n' : ''),
|
||||
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)
|
||||
}
|
||||
|
||||
function makeFunction (code) {
|
||||
function makeFunction (code, errors) {
|
||||
try {
|
||||
return new Function(code)
|
||||
} catch (e) {
|
||||
} catch (err) {
|
||||
errors.push({ err, code })
|
||||
return noop
|
||||
}
|
||||
}
|
||||
|
@ -406,6 +406,8 @@ describe('Directive v-for', () => {
|
||||
})
|
||||
|
||||
it('should warn component v-for without keys', () => {
|
||||
const warn = console.warn
|
||||
console.warn = jasmine.createSpy()
|
||||
new Vue({
|
||||
template: `<div><test v-for="i in 3"></test></div>`,
|
||||
components: {
|
||||
@ -414,8 +416,10 @@ describe('Directive v-for', () => {
|
||||
}
|
||||
}
|
||||
}).$mount()
|
||||
expect('<test v-for="i in 3">: component lists rendered with v-for should have explicit keys')
|
||||
.toHaveBeenWarned()
|
||||
expect(console.warn.calls.argsFor(0)[0]).toContain(
|
||||
`<test v-for="i in 3">: component lists rendered with v-for should have explicit keys`
|
||||
)
|
||||
console.warn = warn
|
||||
})
|
||||
|
||||
it('multi nested array reactivity', done => {
|
||||
|
Loading…
Reference in New Issue
Block a user