从脚手架Vue CLI开始——vue-router——Promise
安装3及以上版本:npm install -g @vue/cli
拉取 2.x 模板 (旧版本)(如果想同时使用2.x版本):npm install -g @vue/cli-init
——Vue CLI >= 3 和旧版使用了相同的 vue
命令,所以 Vue CLI 2 (vue-cli
) 被覆盖了。如果你仍然需要使用旧版本的 vue init
功能,你可以全局安装一个桥接工具
Vue CLI2初始化项目:vue init webpack my-project
Vue CLI3初始化项目:vue creat my-project
1.vue init webpack vuecli2test
2.初始化参数详解:
3.安装完成后:npm run dev ,结果:
目录详解:
2. node可以直接运行js文件,如node test.js,node.js其实就是一个基于 Chrome V8 引擎的 JavaScript 运行环境
编译过程 | 性能对比 | |
runtime-compiler | template -> ast(抽象语法树) -> render -> virtual DOM -> UI | 低(代码量多) |
runtime-only | render -> virtual DOM -> UI | 高(代码量少) |
runtimeonly直接从render开始渲染,少了template -> ast ->render的过程,这个过程是由"vue-template-compiler": "^2.5.2",进行解析的 |
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
// //2.高级用法,传入组件对象
// const Cpn = {
// template:'{{message}}',
// data(){
// return {message:'我是组件message'}
// }
// }
/* eslint-disable no-new */
new Vue({
el: '#app',
//0.runtimecompiler的写法:
// components: { App },
// template: ' ' //上边这两行等于下边这个过程
//1.普通用法:自己创建标签
// render:function(createElement){
// return createElement('h2',
// {class:'box'},
// ['hello world',createElement('button',['按钮'])]);
// }
//2.高级用法,传入组件对象
// render:function(createElement){
// return createElement(Cpn)
// }
//3.so:runtimeonly的写法传入App组件:
render:h =>h(App)
})
new Vue({
render: h => h(App),
}).$mount('#app')
//等价写法:
new Vue({
el:'app',
render:h=>h(App)
})
前端渲染和后端渲染(非常重要)
后端渲染(第一个阶段) | 前端渲染(也是前后端分离,属于第二个阶段,Ajax技术) | SPA页面(第三个阶段) | |
定义 | sp:java server page 早起的网站开发整个HTML页面都是由服务器渲染的,服务器直接渲染生产好的HTML页面,返回给客户端进行显示 |
后端只负责提供数据,不负责任何界面的内容; 浏览器中显示的网页内容,都是由前端写的js代码在浏览器中执行,最终渲染出来的网页 |
SPA:single page web application单页面富应用,整个网站只有一个HTML页面,只请求一次HTML+CSS+JS |
服务处理过程 | 服务处理过程: 1.⼀个⻚⾯有⾃己对应的网址, 也就是URL. 3.Controller进⾏各种处理, 最终⽣成HTML或者数据, 返回给前端. |
1.前端输入地址url,如jd.com/nanzhuang 2.根据url去静态资源库请求HTML+CSS+JS代码,渲染页面 3.根据URL去API接口服务器拿数据list["",""] 4.前端解析Ajax请求的数据,创建HTML元素,显示请求的数据 |
1.根据URL去静态资源库请求HTML+CSS+JS(全部) 2.根据点击的不同位置(请求不同的URL),请求不同的组件渲染页面,这种映射关系,是由前端路由来管理的(一个URL对应一个组件VUE) (所以说点击别的地方,页面不会刷新,因为只请求一次) |
缺点 | 1.整个页面模块由后端人员编写和维护 2.前端人员如何开发按页面,需要PHP和Java来开发页面 3.HTML代码和数据以及对应的逻辑混在一起,编写和维护都是非常糟糕的事 |
前端路由 | 后端路由 |
后端处理URL和页面之间的映射关系 |
第二个阶段:前后端分离阶段示意图:
第三个阶段:SPA单页面富应用阶段:
loaction.hash(方法一) | history.pushState( { },' ','home')(方法二) | history.replaceState({},'','about') | |
概念 | url的hash其实就是锚点(#),本质上是改变window.location的href属性 | html5的方式,通过栈结构,先进后出原则,浏览器每次返回都返回最后放入的数据。 | 直接替换URL,不能返回 |
详情 | 1. loaction.hash = 'aaa' 2. 网页变成:http://localhost:8081/#/aaa 但是内容并没有重新请求资源(network查看) 注意:location.href = 'aaa',页面是会发生刷新的 |
1. history.pushState( { },' ','home') 2. history.pushState( { },' ','about') 3.history.pushState( { },' ','me') 4.以上三个支持栈结构,如果用history.back()执行(相当于浏览器的返回),从后往前返回,第一次http://localhost:8081/about #/ |
|
补充 | history.back()=history.go(-1) |
1.创建路由组件(component文件夹创建home.vue和about.vue)
2.配置路由映射:组件和路径映射关系
3.使用路由,在app.vue使用和,是vue-router内置的组件,它会被渲染成一个标签,根据当前标签动态渲染不同的组件
//router/index.js
import VueRouter from 'vue-router'
import Vue from 'vue'
//1.通过Vue.use()插件,安装插件
Vue.use(VueRouter);
//2.创建VueRouter对象
const routes = [ //这里是一个映射关系
{
path:'',
redirect:'/home' //重定向,默认打开的是home组件内容
},{
path:'/home',
component:Home
},{
path:'/about',
component:About
}
]
const router = new VueRouter({
routes
})
//3.将router对象导出到Vue实例
export default router
//main.js
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
//App.vue
2)App.Vue配置:用户 和 data(){return { userId:'lisi'}}
3)User.vue配置:
和computed:{userId(){return this.$route.params.userId //返回的是当前活跃(点击)的组件}}
3.懒加载的方式(第三个为主):
方式一 (结合Vue的异步组件和Webpack的代码分析) |
const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })}; |
方式二 (AMD写法) |
const About = resolve => require(['../components/About.vue'], resolve); |
方式三(重要) (ES6中,更简单写法组织Vue异步组件和webpack的代码分割) |
const Home = () => import('../components/Home.vue') |
//1. HomeNews.vue和HomeMessage.vue提前写好
//2. index.js配置:
const routes = [{
path: '',
redirect: '/home'
}, {
path: '/home',
component: () => import ('../components/Home'),
children: [{
path: 'news',
component: () => import ('../components/HomeNews'),
}, {
path: 'message',
component: () => import ('../components/HomeMessage'),
}]
}, {
path: '/about',
component: () => import ('../components/About')
}, {
path: '/user/:userId',
component: () => import ('../components/User')
}]
//3.Home.vue中配置router-link和router-view
我是首页
我是首页的内容
新闻
消息
App.vue: | 用户 |
User.vue: | {{$route.params.id}} |
App.vue: | 我的 |
Profile.vue: |
{{$route.query}}
{{$route.query.name}} |
created |
new Vue({})组件创建完的时候回调 |
mounted |
template挂载到dom上面之后,执行的函数 |
updated |
界面发生刷新的时候 (如点击 {{message}}) |
4.在点击home,about,user子组件的时候,才会被创建,才会显示各自.vue里的created函数:
5.因此,可以在各子组件的created中,使用 document.title = '首页' 设置跳转后的标题
6.但是4的方法过于繁琐,可以使用全局导航守卫,在index.js中使用beforeEach函数:
参数:to:即将进入的目标路由对象,from当前导航将要离开的路由对象,next:调用方法后才能进入下一个钩子。
补充一:如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数.
补充二: 上面我们使用的导航守卫, 被称之为全局守卫. 路由独享的守卫. 组件内的守卫.
2.keep-alive是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
|
3.保留首页的消息内容的做法:
1)index.js中把重新定向关掉redirect;
2)App.vue中设置缓存:
3)Home.vue组件中,设置actived()用于触发激活时的状态,beforeRouterLeave()用于记录离开之前的路径,如下Home.vue:
export default {
name:'Home',
data(){
return {
message:'你好',
path:'/home/news'
}
},
//这两个组件对应的是
created() { //组件创建完的时候回调
console.log('home created')
},
destroyed() {
console.log('home destroyed')
},
//这两个函数对应的是 ,
//加了keep-alive后,created只执行一次,destroyed不执行
activated() {
console.log('home activated');
this.$router.push(this.path)
},
deactivated() {
console.log('home deactivated');
},
beforeRouteLeave (to, from, next) {
console.log('beforeRouteLeave',this.$route.path); //beforeRouteLeave /home/message
this.path = this.$route.path;
next()
},
}
1.keep-alive是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。它的两个关键属性:include、exclude:
include | 字符串串或正则表达,只有匹配的组件会被缓存 |
exclude | 字符串串或正则表达式,任何匹配的组件都不不会被缓存 |
2.用法:(App.vue配置如下,Profile和User分别为组件到导出时名字)
1.引入样式:css放到/src/assets/css文件夹中,引入的时候在各组件的style中通过import导入:
2.App.vue核心代码,搭建导航栏:
App.vue <- TabBar.vue <- TabBarItem.vue,核心代码如下:
注:就是TabBar导出的组件,驼峰要改为"-"
1.App.vue代码:
首页
分类
购物车
我的
2.TabBar.vue代码
3.TabBarItem代码
因为slot是会被替换的,因此不能在slot内部写class,正确写法是外层包一层div:
1.注意main.js在实例化的时候别忘加注册router,此router是index.js导出的;
2.mode:"history",别忘记加“”
3,核心代码:
//App.vue核心部分:
首页
......
//TabBar.vue
//TabBarItem.vue
...
props:{ path:String},
...
btnClick(){ this.$router.replace(this.path) }
//Home.vue
首页
1.this.$route.path用于监听处于活跃状态的组件,并从index.js中拿组件的path
2.核心代码;
//TabBarItem.js
...
props:{
path:String,
activeColor:{
type:String,
default:"red"
}
},
...
computed:{
isActive(){//$route是处于活跃状态的路由
return this.$route.path == this.path
},
activeStyle(){
return this.isActive ? {color:this.activeColor}:{}
}
},
//App.vue
首页
App.vue封装之前:
首页
分类
...
App.vue封装之后(main-tab-bar为MainTabBar.vue组件):
//App.vue
//MainTabBar.vue
首页
分类
购物车
我的
注:src引用的,除了@以外,其他的要加~,如下:
...
1.Promise是异步编程的一种解决方案。
2.那什么时候我们会来处理异步事件呢?
一种很常见的场景应该就是网络请求了。 我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回。 所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。 如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。
网络请求非常复杂的时候,会出现回调地狱,即回调函数里又需要回调 。
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步1")
console.log("异步1")
setTimeout(() => {
console.log("异步2")
console.log("异步2")
setTimeout(() => {
console.log('异步3');
console.log('异步3');
}, 2000);
}, 2000);
}, 2000);
})
3.Promise可以以一种更优雅的方式解决这个问题。
1)内部加resolve(),外部加.then
2)Promise实际是把网络请求代码和处理代码(.then())进行了分离
//3.改写Promise,链式编程
new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000);
}).then(() => {
console.log("异步4")
console.log("异步4")
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000);
})
}).then(() => {
console.log("异步5")
console.log("异步5")
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000);
})
}).then(() => {
console.log('异步6');
console.log('异步6');
})
4.什么情况下会用到Promise:异步操作时
5.resolve传值(成功时执行then)和reject捕捉(错误时执行.catch),也可以合写:then(resolve,reject):
//4.resolve()传值
new Promise((resolve, reject) => {
setTimeout(() => {
//成功时调用resolve,然后执行then
resolve('resolve传值');
//失败时调用reject,并且不会执行then,会执行catch,然后错误信息传给err
reject('error message');
}, 2000);
}).then((data) => {
console.log(data);
console.log(data);
}).catch(err => {
console.log(err);
})
//5.then(()=>{},()=>{})
new Promise((resolve, reject) => {
setTimeout(() => {
//成功时调用resolve,然后执行then
resolve('resolve传值合写');
//失败时调用reject,并且不会执行then,会执行catch,然后错误信息传给err
reject('error message合写');
}, 2000);
}).then((data) => {
console.log(data);
console.log(data);
}, err => {
console.log(err)
})
sync -> 同步 synchronization
async ->异步 asynchronization
1.Promise三种状态
pending: | 等待状态,比如正在进行网络请求,或者定时器没有到时间。 |
fulfill: | 满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then() |
reject: | 拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch() |
逐级抽取和两种错误抛出方法
1.需求:有时候需要两个请求全部实现才能执行下一步,传统的做法需要对两个请求进行判断,如下:
2.Promise的all方法,Promise([{new...},{new...}]).then(results => {console.log(results)})
Promise.all([
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({name:'ws',age:18})
}, 10000);
}),
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({name:'yz',age:16})
}, 20000);
}),
]).then(results =>{
console.log(results); //输出[{...},{...}]
})