动态组件
<component v-bind:is=""></component>
实现动态组件。
<body>
<div id="root">
<component v-bind:is="currentId"></component>
<button v-on:click="handleClick">toggle</button>
</div>
</body>
import Vue from "vue";
Vue.component("one-component",{
template:"<div>hello</div>",
mounted:function(){
console.log("hello:enter mounted")
}
});
Vue.component("two-component",{
template:"<div>world</div>",
mounted:function(){
console.log("world:enter mounted");
}
})
const vm = new Vue({
el:"#root",
data:{
currentId:"one-component"
},
methods:{
handleClick:function(){
if(this.currentId === "one-component"){
this.currentId = "two-component";
}else if(this.currentId === "two-component"){
this.currentId = "one-component";
}
}
}
});
首次加载网页,渲染"hello"所在组件;
从"hello"到"world",渲染"world"所在组件;
从"world"回到"hello",重新渲染"hello"所在组件;
从"hello"再到"world",重新渲染"world"所在组件;
怎么保存组件状态,避免反复重新渲染组件?keep-alive
。
keep-alive
<body>
<div id="root">
<keep-alive>
<component v-bind:is="currentId"></component>
</keep-alive>
<button v-on:click="handleClick">toggle</button>
</div>
</body>
使用<keep-alive></keep-alive>
后,"hello"所在组件、"world"所在组件仅在首次加载时渲染了一次,随后的切换没有重新渲染。
异步组件
<body>
<div id="root">
<my-component></my-component>
</div>
</body>
import Vue from "vue";
Vue.component("my-component",function(resolve){
setTimeout(function(){
resolve({
template:"<div>hello world</div>"
})
},3000);
}
);
const vm = new Vue({
el:"#root"
});
动态导入
<!-- index.html -->
<body>
<div id="root">
<home></home>
</div>
</body>
//index.js
import Vue from "vue";
import home from "./home.vue";
const vm = new Vue({
el:"#root",
render:h => h(home)
});
//home.vue
<template>
<div>
<async-welcome v-if="isShow"/>
<button v-on:click="handleClick">login</button>
</div>
</template>
<script>
export default {
data:function(){
return {
isShow:false
}
},
components:{
"async-welcome":() => import("./welcome")
},
methods:{
handleClick:function(){
this.isShow = true;
}
}
}
</script>
//welcome.vue
<template>
<div>welcome</div>
</template>
处理加载状态
服务端
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node ./app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^4.3.0",
"express": "^4.17.1",
"html-webpack-plugin": "^4.4.1",
"http-proxy-middleware": "^1.0.5",
"koa": "^2.13.0",
"koa-server-http-proxy": "^0.1.0",
"koa-webpack-dev-middleware": "^2.0.2",
"style-loader": "^1.2.1",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-dev-middleware": "^3.7.2"
},
"dependencies": {
"vue": "^2.6.12"
}
}
//webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin} = require("clean-webpack-plugin");
const {VueLoaderPlugin} = require("vue-loader");
module.exports = {
mode:"development",
devtool:"cheap-source-map",
entry:{
"index":"./src/index.js"
},
output:{
filename:"[name].bundle.js",
path:path.join(__dirname,"dist"),
publicPath:"/"
},
resolve:{
modules:[path.join(__dirname,"node_modules")],
extensions:[".vue",".js",".json"],
alias:{
"vue$":"vue/dist/vue.esm.js"
}
},
module:{
rules:[
{
test:/\.vue$/,
include:/src/,
use:["vue-loader"]
},
{
test:/\.js$/,
use:{
loader:"babel-loader",
options:{
presets:["@babel/preset-env"]
}
}
},
{
test:/\.css$/,
use:["style-loader","css-loader"]
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:"./src/index.html"
}),
new CleanWebpackPlugin(),
new VueLoaderPlugin()
]
}
服务端的搭建既可以用Express,也可以用Koa。
需要注意的是,这两种框架使用的中间件不同,需要适配,比如
Express
+webpack-dev-middleware
+http-proxy-middleware
,
Koa
+koa-webpack-dev-middleware
+koa-server-http-proxy
- 基于Express的服务器端搭建
const webpack = require("webpack");
const config = require("./webpack.config.js");
const compiler = webpack(config);
const express = require("express");
const app = express();
const webpackDevMiddleware = require("webpack-dev-middleware");
app.use(webpackDevMiddleware(compiler,{
publicPath:config.output.publicPath
}));
const {createProxyMiddleware} = require("http-proxy-middleware");
app.use("/data",createProxyMiddleware({
target:"http://www.weather.com.cn",
changeOrigin:true
}));
app.listen(3000,function(){
console.log("listening on *:3000");
});
- 基于Koa的服务端搭建
const webpack = require("webpack");
const config = require("./webpack.config.js");
const compiler = webpack(config);
const Koa = require("koa");
const app = new Koa();
const webpackDevMiddleware = require("koa-webpack-dev-middleware");
app.use(webpackDevMiddleware(compiler,{
publicPath:config.output.publicPath
}));
const httpProxy = require("koa-server-http-proxy");
app.use(httpProxy("/data",{
target:"http://www.weather.com.cn",
changeOrigin:true
}))
app.listen(3000,function(){
console.log("listening on *:3000");
});
客户端
<!-- index.html -->
<body>
<div id="root">
<my-component></my-component>
</div>
</body>
//index.js
import Vue from "vue";
import loadingComponent from "./loading-component.vue";
import errorComponent from "./error-component.vue";
Vue.component("my-component",() => ({
component: import('./weather-component.vue'),
loading: loadingComponent,
error: errorComponent,
}))
const vm = new Vue({
el:"#root"
});
// error-component.vue
<template>
<div>天气信息加载失败!</div>
</template>
// loading-component.vue
<template>
<div>正在加载天气信息...</div>
</template>
// weather-component.vue
<template>
<div v-if="isShow">
<div>所在城市:{{city}}</div>
<div>今日天气:{{weather}}</div>
<div>最低气温:{{temp1}}</div>
<div>最高气温: {{temp2}}</div>
</div>
</template>
<script>
export default {
data:function(){
return {
isShow:false,
city:"",
weather:"",
temp1:"",
temp2:""
}
},
mounted:function(){
const self = this;
fetch("data/cityinfo/101240101.html")
.then(function(response){
return response.json();
}).then(function({weatherinfo}){
const {city,weather,temp1,temp2} = weatherinfo;
self.isShow = true;
self.city = city;
self.weather = weather;
self.temp1 = temp1;
self.temp2 = temp2;
})
}
}
</script>
error: errorComponent
Vue.component("my-component",() => ({
component: import('./weather-component.vue'),
loading: loadingComponent,
error: errorComponent,
delay:200,
timeout:1,
}))
timeout
默认是Infinite
,我们试试把timeout
设置得很小,比如1
。
如果异步组件加载超时,则会显示出errorComponent
。
loading: loadingComponent
function getComponent(){
return new Promise((resolve) => {
setTimeout(() => {
resolve(import("./weather-component.vue"));
},1000);
})
}
Vue.component("my-component",() => ({
component:getComponent(),
loading: loadingComponent,
error: errorComponent
}))
在异步组件还没有加载完成的这段时间里,显示loadingComponent
。
在上述代码中,我们调用setTimeout
延时1s
加载异步组件,在延时的这样时间里,我们看到的是loadingComponent
的内容。
参考文档
vue之异步组件
使用webpack-dev-middleware
npm之koa-webpack-dev-middleware
github之koa-webpack-dev-middleware
koa代理之koa-server-http-proxy