createApp
返回一个提供应用上下文的应用实例。应用实例挂载的整个组件树共享同一个上下文。
- 参数: 该函数接收一个根组件选项对象作为第一个参数:
const app = createApp({
data() {
return {
...
}
},
methods: {...},
computed: {...}
...
})
使用第二个参数,我们可以将根 prop 传递给应用程序:
const app = createApp(
{
props: ['username']
},
{ username: 'Evan' }
)
<div id="app">
<!-- 会显示 'Evan' -->
{{ username }}
</div>
- 核心源码:
// 创建一个Vue实例 -- createApp(公共API)
const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args);
{
injectNativeTagCheck(app);
}
const { mount } = app;
// 重新定义mount挂载元素 e.g. createApp({ data: {} }).mount("#app")
app.mount = (containerOrSelector) => {
const container = normalizeContainer(containerOrSelector);
if (!container)
return;
// createApp创建Vue实例传递的options
const component = app._component;
if (!isFunction(component) && !component.render && !component.template) {
component.template = container.innerHTML;
}
// 重置content,在mounting之前
container.innerHTML = '';
// 执行app原始的mount挂载元素
const proxy = mount(container);
// v-clock的作用是防止页面在加载时出现
container.removeAttribute('v-cloak');
container.setAttribute('data-v-app', '');
return proxy;
};
return app;
});
// 确保renderer渲染函数存在
function ensureRenderer() {
return renderer || (renderer = createRenderer(rendererOptions));
}
/**
* 创建普通的render函数
*/
function createRenderer(options) {
return baseCreateRenderer(options);
}
/**
* 创建普通的render函数,可以接收两个泛型参数:
*
* 自定义渲染器可以在平台中传递特定类型,像这样:
*
* ``` js
* const { render, createApp } = createRenderer<Node, Element>({
* patchProp,
* ...nodeOps
* })
* ```
*/
function baseCreateRenderer(options, createHydrationFns) {
// ...
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
};
}
/**
* 创建renderAPI
* @param {Function} render render函数
* @param {Boolean} hydrate 是否为服务端渲染
* @returns
*/
function createAppAPI(render, hydrate) {
// Vue.createApp(options, rootProps) => 第一个参数为options,第二个参数为rootProps
return function createApp(rootComponent, rootProps = null) {
// 如果传递了第二个参数,则第二个参数必须为一个对象
if (rootProps != null && !isObject(rootProps)) {
warn(`root props passed to app.mount() must be an object.`);
rootProps = null;
}
// 创建上下文对象 => { app, config: {}, mixins: [], components: {}, directives: {}, provides: {} }
const context = createAppContext();
const installedPlugins = new Set();
// 是否已挂载
let isMounted = false;
// 返回一个app对象
const app = (context.app = {
_uid: uid$1++,
_component: rootComponent,
_props: rootProps,
_container: null,
_context: context,
version,
get config() {
return context.config;
},
set config(v) {
{
warn(`app.config cannot be replaced. Modify individual options instead.`);
}
},
// ...
// 应用API
// ...
// 开始挂载元素
// e.g. app.mount("#app")
mount(rootContainer, isHydrate /* 是否水合,服务端渲染 */) {
if (!isMounted) {
// 创建VNode
const vnode = createVNode(rootComponent, rootProps);
vnode.appContext = context;
{
context.reload = () => {
render(cloneVNode(vnode), rootContainer);
};
}
if (isHydrate && hydrate) {
hydrate(vnode, rootContainer);
}
else {
// 开始render
render(vnode, rootContainer);
}
isMounted = true;
app._container = rootContainer;
rootContainer.__vue_app__ = app;
{
devtoolsInitApp(app, version);
}
return vnode.component.proxy;
}
else {
warn(`App has already been mounted.\n` +
`If you want to remount the same app, move your app creation logic ` +
`into a factory function and create fresh app instances for each ` +
`mount - e.g. \`const createMyApp = () => createApp(App)\``);
}
},
});
return app;
};
}