vue
文章目录
写在开头,学习一门技术最好沉浸在官网: vue官方文档
前端三要素
HTML(结构):超文本标记语言(Hyper Text Markup Language),决定网页的结构和内容
CSS(表现):层叠样式表(Cascading Style Sheets),设定网页的表现样式
JavaScript(行为):是一种弱类型脚本语言,其源代码不需经过编译,而是由浏览器解释运行,
用于控制网页的行为
前后端分离演变历史
MVC:
为了降低开发的复杂度,以后端为出发点,比如:Struts2、SpringMVC 等框架的使用,就是后端的 MVC
时代;
优点
MVC 是一个非常好的协作模式,能够有效降低代码的耦合度,从架构上能够让开发者明白代码应该写在
哪里。为了让 View 更纯粹,还可以使用 Thymeleaf、Freemarker 等模板引擎,使模板里无法写入 Java
代码,让前后端分工更加清晰。
缺点
前端开发重度依赖开发环境,开发效率低,这种架构下,前后端协作有两种模式:
第一种是前端写 DEMO,写好后,让后端去套模板。好处是 DEMO 可以本地开发,很高效。不足是还需要后端套模板,有可能套错,套完后还需要前端确定,来回沟通调整的成本比较大;
另一种协作模式是前端负责浏览器端的所有开发和服务器端的 View 层模板开发。好处是 UI 相关的代码都是前端去写就好,后端不用太关注,不足就是前端开发重度绑定后端环境,环境成为影响前端开发效率的重要因素。
前后端职责纠缠不清:模板引擎功能强大,依旧可以通过拿到的上下文变量来实现各种业务逻辑。
这样,只要前端弱势一点,往往就会被后端要求在模板层写出不少业务代码。还有一个很大的灰色地带是 Controller ,页面路由等功能本应该是前端最关注的,但却是由后端来实现。
Controller 本身与 Model 往往也会纠缠不清,看了让人咬牙的业务代码经常会出现在Controller 层。这些问题不能全归结于程序员的素养,否则 JSP 就够了。
对前端发挥的局限性:性能优化如果只在前端做空间非常有限,于是我们经常需要后端合作,但由于后端框架限制,我们很难使用 【Comet】 、 【BigPipe】 等技术方案来优化性能。
基于 AJAX 带来的 SPA 时代
时间回到 2005 年 AJAX (Asynchronous JavaScript And XML,异步 JavaScript 和 XML,老技术新用法)
被正式提出并开始使用 CDN 作为静态资源存储,于是出现了 JavaScript 王者归来(在这之前 JS 都是用 来在网页上贴狗皮膏药广告的)的 SPA(Single Page Application)单页面应用时代。
这种模式下,前后端的分工非常清晰,前后端的关键协作点是 AJAX 接口。看起来是如此美妙,但回过头来看看的话,这与 JSP 时代区别不大。复杂度从服务端的 JSP 里移到了浏览器的 JavaScript,浏览器端变得很复杂。类似 Spring MVC,这个时***始出现浏览器端的分层架构:MVC,MVP,MVVM
缺点
前后端接口的约定: 如果后端的接口一塌糊涂,如果后端的业务模型不够稳定,那么前端开发会很痛苦;不少团队也有类似尝试,通过接口规则、接口平台等方式来做。**有了和后端一起沉淀的接口规则 ,还可以用来模拟数据,使得前后端可以在约定接口后实现高效并行开发。
前端开发的复杂度控制: SPA 应用大多以功能交互型为主,JavaScript 代码过十万行很正常。大量 JS 代码的组织,与 View 层的绑定等,都不是容易的事情。
前端为主的 MV* 时代
此处的 MV* 模式如下:
MVC(同步通信为主):Model、View、Controller
MVP(异步通信为主):Model、View、Presenter
MVVM(异步通信为主):Model、View、ViewModel
为了降低前端开发复杂度,涌现了大量的前端框架,比如: AngularJS 、 React 、 Vue.js 、
EmberJS 等,这些框架总的原则是先按类型分层,比如 Templates、Controllers、Models,然后再在层内做切分,如下图:
优点
前后端职责很清晰: 前端工作在浏览器端,后端工作在服务端。清晰的分工,可以让开发并行,测试数据的模拟不难,前端可以本地开发。后端则可以专注于业务逻辑的处理,输出 RESTful。
前端开发的复杂度可控: 前端代码很重,但合理的分层,让前端代码能各司其职。这一块蛮有意思的,简单如模板特性的选择,就有很多很多讲究。并非越强大越好,限制什么,留下哪些自由, 代码应该如何组织,所有这一切设计,得花一本书的厚度去说明。
部署相对独立: 可以快速改进产品体验
缺点
代码不能复用。比如后端依旧需要对数据做各种校验,校验逻辑无法复用浏览器端的代码。如果可以复用,那么后端的数据校验可以相对简单化。全异步,对 SEO 不利。往往还需要服务端做同步渲染的降级方案。
性能并非最佳,特别是移动互联网环境下。
SPA 不能满足所有需求,依旧存在大量多页面应用。URL Design 需要后端配合,前端无法完全掌 控。
NodeJS 带来的全栈时代
前端为主的 MV* 模式解决了很多很多问题,但如上所述,依旧存在不少不足之处。随着 NodeJS 的兴起,JavaScript 开始有能力运行在服务端。
但是这与jsp有什么区别呢。
为什么要使用 Vue.js
-
轻量级,体积小是一个重要指标。Vue.js 压缩后有只有 30多kb (Angular 压缩后 56kb+,React 压缩后 44kb+)
-
移动优先。更适合移动端,比如移动端的 Touch 事件
-
易上手,学习曲线平稳,文档齐全
-
吸取了 Angular( 模块化 )和 React( 虚拟 DOM )的长处,并拥有自己独特的功能,如: 计算属 性开源,社区活跃度高。
Vue.js属于MVVM架构中的viewmodel
Vue.js 的两大核心要素
数据驱动
当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用
Object.defineProperty 把这些属性全部转为 getter/setter 。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。
这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通
知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你
可能需要安装 vue-devtools 来获取更加友好的检查接口。
每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖
项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
组件化
页面上每个独立的可交互的区域视为一个组件
每个组件对应一个工程目录,组件所需的各种资源在这个目录下就近维护
页面不过是组件的容器,组件可以嵌套自由组合(复用)形成完整的页面
试水(入门代码)
引用vue,可以直接使用官方的cdn:
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
也可以自己下载到本地,放到项目中:这里用hbuilder,直接给我弄好环境了。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<p>{
{msg}}</p>
</div>
<script type="text/javascript">
var app=new Vue({
el:"#app",
data:{
msg:"ppl沙雕"
}
});
</script>
</body>
</html>
vue对象:
- el:绑定的元素,实例是绑定id为app的元素,此元素中可以直接使用vue对象中data定义的变量名取到值
- data: 数据,绑定元素内可用变量名访问
- methods:元素内使用:v-on:事件 绑定事件属性名
- 钩子函数
生命周期
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载 DOM、渲染→更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就是生命周期。
在 Vue 的整个生命周期中,它提供了一系列的事件,可以让我们在事件触发时注册 JS 方法,可以让我们用自己注册的 JS 方法控制整个大局,在这些事件响应方法中的 this 直接指向的是 Vue 的实例。
作为扑街后端,这里我们不需要了解那么多,mounted:初始化函数。
beforeCreate
在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
created
实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和
方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
beforeMount
在挂载开始之前被调用:相关的 render 函数首次被调用。
mounted
el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,
这不会触发附加的重渲染过程。
updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情
况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不
被调用。
beforeDestroy实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件***会被移除,所
有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
语法使用
条件判断
v-if:相当于java的if
v-else-if:相当于java的else if
v-if:相当于java的else
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
<p v-if="grade<0">小老弟这分数不对啊</p>
<p v-else-if="grade<60">小老弟不行啊</p>
<p v-else-if="grade<80">小老弟很一般啊</p>
<p v-else-if="grade<=100">小老弟可以啊</p>
<p v-else>你这分数不对啊</p>
</div>
<script>
var app=new Vue({
el:"#app",
data:{
grade: 80
}
});
</script>
</body>
</html>
由于vue的数据在控制台不好看,这里我们搜索vue插件:vue devtools
在控制台分别修改为59,101,100
的,但是实例就懒得弄了。
列表遍历
v-for=“表达式”,类似增强for循环,表达式:item in items
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>遍历</title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<ol id="ol">
<li v-for="one in pandas">{
{one}}</li>
</ol>
<ul id="table">
<li v-for="ap in pandasObj">{
{ap.name}}---{
{ap.age}}</li>
</ul>
</body>
<script>
var ol=new Vue({
el:"#ol",
data:{
pandas:[
'胖大海',
'雪宝',
'金虎'
]
}
});
var table=new Vue({
el:"#table",
data:{
pandasObj:[
{
name:'胖大海',
age:3
},{
name:'雪宝',
age:2
},{
name:'金虎',
age:4
}
]
}
});
</script>
</html>
计算属性
计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 响应函数操作的数据
还没有发生改变,多次访问 cul
计算属性会立即返回之前的计算结果,而不必再次执行函数,而这里时间函数明显没有调用data中数据,所以它结果不会变。
这里我们做个对比:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>计算属性</title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="app">
普通:{
{pt()}}<br />
计算:{
{cul}}
</div>
</body>
<script>
var app=new Vue({
el:"#app",
data:{
num:0
},
methods:{
pt : function(){
return new Date();
}
},
computed:{
cul:function(){
return new Date();
}
}
});
</script>
</html>
普通方法调用两次,数据都不一样,而计算属性,计算值两次都一样。
监听属性
这里使用官方的说法。
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
——特别是如果你之前使用过 AngularJS。
<div id="demo">{
{ fullName }}</div>
<script>var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})</script>
在控制台修改firstName或lastName,fullName的值都会变。当值改变时,属性同名的监听方法就会被调用,效果类似于计算属性:
<div id="demo">{
{ fullName }}</div>
<script>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
</script>
简洁了很多。
双向数据绑定
使用v-model
表单数据绑定vue对象中data的属性,data的属性又有其他引用
<input v-model="text" type="text" /><br />
<p>{
{text}}</p>
</div>
<script type="text/javascript">
Vue.component("luoweifei",{
props: ['url','netname'],
template: "<li><a :href='url'>{
{netname}}</a></li>"
});
var app=new Vue({
el:"#app",
data:{
text:""
}
});
</script>
取值
标签外取值:
<div id="app">
<p>{
{msg}}</p>
</div>
<script type="text/javascript">
var app=new Vue({
el:"#app",
data:{
msg:"ppl沙雕"
}
});
</script>
属性取值
<div id="app">
<a :href="url">{
{msg}}</a>
</div>
<script type="text/javascript">
var app=new Vue({
el:"#app",
data:{
msg:"我的git",
url:"http://lwfqw.git"
}
});
</script>
:href
等同于v-bind:href
可以绑定vue中的data到属性中。
路由
设置路由,每个路由有下列属性:
name:取的名字
path:路由路径
component: 页面模块(vue文件)
props: true (允许通过属性传值)
children:子路由,该页面中的router link标签path对应的路由
路由映射
vue中js和vue文件都可以作为模块被导入。导入的vue文件可以作为路由对象中的component的值与path属性唯一映射路由。
import Vue from "vue";
import Router from "vue-router";
import Login from "../components/Login";
import Main from "../components/main";
import HelloWorld from "../components/HelloWorld";
import userinfo from '../components/userinfo';
import userlist from '../components/user/list';
import jingduShow from '../components/jindu/show';
import jdadd from '../components/jindu/jdadd';
Vue.use(Router);
export default new Router({
//路由模式,history模式没有#,hash模式有#,默认
mode: 'history',
routes: [
//登录
{
path: '/login',
name: 'Login',
component: Login
},
//首页
{
path: '/hello',
name: 'hello',
component: HelloWorld,
children: [
{
path: '/userinfo/:id',
name: 'userinfo',
component: userinfo,
props: true
},{
path:'/userlist',
name: 'userlist',
component: userlist
},{
path:'/show',
name:'jingdushow',
component:jingduShow
},{
path:'/jdadd',
name:'jdadd',
component:jdadd
}
]
},
{
path: '/main',
name: 'Main',
component: Main
},
//重定向到main
{
path: '/goMain',
redirect: '/main'
}
]
})
跳转页面
跳转同级: 通过this.$router.push("/hello");
跳转到同级的/hello
;
页面某部位显示子路由页面:<router-link to="/userlist">用户列表</router-link>显示页面(/userlist对应路由映射vue文件)到<router -view />
中;注意:使用router-link
,to属性对应的路由必须为主体页面(router-link标签所在页面)的子路由。
携带值挑战页面或路由
路径携参:
路由中: {
path: ‘/userinfo/:id’,
name: ‘userinfo’,
component: userinfo,
props: true
}
:id
就是占位目标路径页面获取值js
this.$route.params.id
,html{ {$router.params.id}}
props携参,修改上面代码为
使用params
路由中: {
path: ‘/userinfo/:id’, //:id占位
name: ‘userinfo’,
component: userinfo,
props: true
}
:id
就是占位目标路径页面获取值js
this.$route.params.id
,html{ {$router.params.id}}
使用query
不需要占位
路由中: {
path: ‘/userinfo’,
name: ‘userinfo’,
component: userinfo,
props: true
}目标路径页面获取值js
this.$route.query.id
,html{ {$router.query.id}}