vue - 写给自己看的教程

文章是从有道云笔记转到csdn博客,如果存在图片丢失问题或者错误,可阅读原文与留言,谢谢!


之前有段时间学习了vue,并做了一个简单项目,隔了半年多再去用vue,已经忘了七七八八了,学习了的东西总要留下一点痕迹吧,也方便后期翻翻看看复习下。

vue相对传统的JS开发,个人感受最大不同,也是算是优势吧

  • 可以单向或双向响应绑定数据,可以避免传统JS频繁操作DOM;
  • 可以组件化,提高UI的复用性;
  • 视图、数据、结构可以很好的分离,更接近MVVM思想;
  • 由于是单页面,加载速度与传统html页面会更快、更流畅,体验更好。

vue项目结构

vue单文件结构

<!--template节点 一级节点-->
<template>
  <div id="container">
    <!--html内容结构区,所有的内容都写这个div容器内,而且只能一个根节点容器-->
    <img alt="Vue logo" src="./assets/logo.png">
    <!--使用子组件 ,当前vue可以称为父组件-->
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
/*通过ES6模块化语句import导入vue组件,又称为子组件  */
import HelloWorld from './components/HelloWorld.vue'

/*export用于声明可以导出vue组件,会在main.js导入使用 ES6模块化语句*/
export default {
  name: 'vue',
  /*声明注册组件,HelloWorld是组件名称,可以在html结构中使用*/
  components: {
    HelloWorld
  },
  created: () => {
  	console.log("App.vue")
  },
  mounted: () => {
  	
  }
}
</script>

<style>
    <!--样式编辑区-->
</style>

既然有父子组件的称呼,当然是少不了两者之间的数据交互,这个后面再介绍。

项目结构

在这里插入图片描述

├── README.md                   项目介绍
├── index.html                  入口页面
├── build                       构建脚本目录
│  ├── build-server.js          运行本地构建服务器,可以访问构建后的页面
│  ├── build.js                 生产环境构建脚本
│  ├── dev-client.js            开发服务器热重载脚本,主要用来实现开发阶段的页面自动刷新
│  ├── dev-server.js            运行本地开发服务器
│  ├── utils.js                 构建相关工具方法
│  ├── webpack.base.conf.js     wabpack基础配置
│  ├── webpack.dev.conf.js      wabpack开发环境配置
│  └── webpack.prod.conf.js     wabpack生产环境配置
├── config                      项目配置
│  ├── dev.env.js               开发环境变量
│  ├── index.js                 项目配置文件
│  ├── prod.env.js              生产环境变量
│  └── test.env.js              测试环境变量
├── mock                        mock数据目录
│  └── hello.js
├── package.json          npm包配置文件,里面定义了项目的npm脚本,依赖包等信息
├── src                   源码目录 
│  ├── main.js            入口js文件
│  ├── app.vue            根组件
│  ├── components         公共组件目录(高复用业务组件、纯样式组件)
│  │  └── title.vue
│  ├── assets             资源目录,这里的资源会被wabpack构建
│  │  └── images
│  │    └── logo.png
│  ├── routes             前端路由
│  │  └── index.js
│  ├── store              应用级数据(state)
│  │  └── index.js
│  └── views              页面目录
│    ├── hello.vue
│    └── notfound.vue
├── static                纯静态资源,不会被wabpack构建。
└── test                  测试文件目录(unit&e2e)
  └── unit                单元测试
    ├── index.js          入口脚本
    ├── karma.conf.js     karma配置文件
    └── specs             单测case目录
      └── Hello.spec.js
  • 基础库: vue.js、vue-router、vuex、whatwg-fetch
  • 编译/打包工具:webpack、babel、node-sass
  • 单元测试工具:karma、mocha、sinon-chai
  • 本地服务器:express

index.html、main.js和App.vue这三者的关系,这里用最简单方式来测试三者的关系,通过日志观察方式可以知道大概的关系:

index.html

<script type="text/javascript">
		console.log("index.html")
</script>

main.js



import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false
console.log('main.js')
//#app 这个ID是index.html中div的
new Vue({
  el: '#app',
  //注册路由,让整个应用拥有路由功能
  router,
  //在声明注册App.vue组件
  components: { App },
  template: '<App/>'
})

App.vue

//vue生命周期created函数
created: () => {
  	console.log("App.vue")
}

最后日志打印:

在这里插入图片描述

  • index.html:是整个项目入口。
  • main.js:是逻辑入口,相当于桥梁,承接着index.html与App.vue的联系。
  • App.vue:第一个组件,组件入口。

https://www.cnblogs.com/Renyi-Fan/p/12484173.html

模板语法

插值

文本

//这个msgText需要在data属性中定义
//在其他地方改变了msgText的值时,span标签也会跟着改变,这就是响应式
<span>{
   
   {msgText}}</span>


data() {
	return {
		msgText: "今天的天气很好",
	}
}

指令

指令一般分为两种,一种是vue自带的,带v-开头的,比如v-bindv-onv-html等,另外一种就是自定义指令,是在子组件中定义了props相关属性,自定义指令与v-bind指令都是简称绑定指令

指令的作用:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。

v-bind

v-bind 指令常用用于响应式地更新 HTML attribute,比如src、href、border等HTML属性,其中:后面就是需要绑定的属性名称。

在这里插入图片描述

//注意img标签,通过v-bind来动态改变值,其中url不能是在`assets`中
<img v-bind:src="url">

<a v-bind:href="url">

另外v-bind指令可以缩写,把冒号前面的v-bind省略,直接写成:

<img :src="url">

<a :href="url">
自定义绑定指令

自定义指令往往用于父组件向子组件的数据传递,指令名称必须在子组件中的props定义声明。比如HelloWorld.vue子组件在App.vue父组件中使用:

HelloWorld子组件

<template>
  <div class="hello">
    <h1>{
   
   { title }}</h1>
	<img v-bind:src="imgUrl" />
 
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  //在props声明属性名称
  props: {
    title: String,
	imgUrl:String,
  }
}
</script>

App.vue父组件

<template>
	<div>
	    <!--3、使用组件  通过:引用子组件定义的属性名称 即指令-->
		<HelloWorld :title="title" :imgUrl="url" />
	</div>
</template>

<script>
    //1、导入子组件,并设置注册组件的名称HelloWorld
	import HelloWorld from './components/HelloWorld.vue'
	export default {
		name: 'app',
		components: {
		    //2、注册导入的组件
			HelloWorld
		},
		data() {
			return {
				title: "今天的天气很好",
				url: "https://private.yinshua86.com/upload_tmp/56219-pexels-valeriia-miller-3685530.jpg?imageView2/2/w/260&attname=pexels-valeriia-miller-3685530.jpg"
			}
		}

	}
</script>

如果使用data属性中的变量,必须需要带:来绑定引用子组件的props指令,data属性的图片URL不能是本地静态链接,必须是网络链接,不然是会失效;如果是绑定的文本属性,可以把:去掉,直接初始化静态值,比如 title="今天天气真好"

v-on

v-on指令一般是用于DOM监听,比如click、copy、change事件等

 <!-- 完整语法 -->
 <button v-on:click="doSomething">点击</button>    
 <!-- 缩写 -->
 <button @click="doSomething">点击</button>

接着在methods属性中定义声明doSomething方法

methods:{
	doSomething(){
		console.log('点击了')
	}
}

data属性

data属性必须直接返回一个已声明初始对象的函数,这个对象也不能间接使用外部的对象。

    //正确用法,使用函数返回对象
    data() {
        return {
            title: 'Hello'
        }
    }

    //错误写法,会导致再次打开页面时,显示上次数据
    data: {
        title: 'Hello'
    }

    //错误写法,同样会导致多个组件实例对象数据相互影响
    const obj = {
        title: 'Hello'
    }
    data() {
        return {
            obj
        }
    }

条件渲染

开发中基本那里都会用到if(…) else(…)条件判断语句,在vue中同样也支持,有两种条件指令

  • v-if和v-else if、v-else
  • v-show

v-if和v-else if、v-else

<template>
	<div>
		<div v-if="score >= 80">成绩很优秀 {
   
   {score}}</div>
		<div v-else-if="score < 80 && score >= 60">成绩良好 {
   
   {score}}</div>
		<div v-else>成绩很差 {
   
   {score}}</div>
		<button v-on:click="doSomething"> 点击</button>
	</div>
</template>

<script>
	export default {
		name: 'app',
		data() {
			return {
				score:100,
			}
		},

		methods:{
			doSomething(){
				console.log('点击了')
				//取0 - 100随机整数
				this.score=Math.ceil(Math.random()*100)
			}
		}

	}
</script>

使用v-if指令DOM元素必须是连续的,如果被分割了运行会不通过的。另外也常常通过v-if指令判断DOM元素是否显示隐藏,前提v-if的条件值为true就会渲染显示,为false时就会销毁DOM元素隐藏。

//当score > 60时就会渲染显示,其他值都会销毁
<div v-if="score > 60">
			<HelloWorld :title="title" :imgUrl="url"  />
</div>

v-if可以设置到template根节点元素上。

v-if切换过程中条件块内的事件监听器和子组件元素会被销毁和重建,每重建一次都会渲染一次,如果频繁的切换会带来性能的开销。

v-show

v-show是根据条件来判断是否显示元素的指令,没有else指令

<div>
	    <div v-show="score >= 80">成绩很优秀 {
   
   {score}}</div>
		<div v-show="score < 80 && score >= 60">成绩良好 {
   
   {score}}</div>
		<HelloWorld :title="title" />
		<div v-show="score > 60">
			<HelloWorld :imgUrl="url"  />
		</div>
		<div v-show="score < 60">成绩很差 {
   
   {score}}</div>
		<button v-on:click="doSomething"> 点击</button>
</div>

从示例可以看出v-show指令在DOM元素中可以不连续的,这点与v-if使用上有所不同。

v-show不管初始条件是什么,元素只会初始渲染一次,并且只是简单地基于 CSS 进行切换,来控制元素的显示和隐藏。

v-if 和 v-show 区别

  • v-if 有更高的切换开销,如果在运行时条件很少改变,则使用 v-if 较好。
  • v-show 有更高的初始渲染开销。如果需要非常频繁地切换,则使用 v-show 较好

列表渲染

列表渲染是基于v-for指令来完成,基本使用如下:

<template>
  <div id="app">
    <div v-for="(item,index) in lists" :key="index">
       <p>{
   
   {index}} - {
   
   {item}}</p>
    </div>
    <div> ------------------------------------------------</div>
    <div v-for="(item,index) in listsObj" :key="index">
      <p>{
   
   {index}} - {
   
   {item.name}} -  {
   
   {item.age}} - {
   
   {item.sex}}</p>
    </div>
  </div>
</template>

<script type="text/ecmascript-6">
  export default {
    name: 'App',
    data(){
      return{
          lists:['张三','李四','王老五','王麻子'],
          listsObj: [{
          id: 0,
          name: '张三',
          age: 28,
          sex: '男'
            }, {
          id: 1,
          name: '李四',
          age: 20,
          sex: '男'
        }]
      }
    },
    created () {
      console.log('App.vue')
    }
    
  }
</script>

上面的列表渲染的数据是来源于数组,数组渲染列表一般是(item,index) in lists 的方式。

  • item:是数组元素的别名,可以随便取名,没有特殊要求
  • index:数组的下标,可选
  • lists: 数据来源,一般在data属性函数中会初始化声明
  • key: key的作用是确保数组元素与html 元素的位置是一致的,保证数据改变时能够准确及时的刷新; key是唯一的,如果是数组用index,如果遍历是对象,需在每个对象元素加上一个唯一id属性。建议使用v-for指令要加上key

当在组件上使用 v-for 时,key是必须的

运行结果:

在这里插入图片描述

遍历对象

<div v-for="(name,value,index) in person" :key="index">
      <p>{
   
   {index}} - {
   
   {name}} - {
   
   {value}}</p>
</div>

 data() {
      return {
        person: {
          id: 0,
          name: '张三',
          age: 28,
          sex: '男'
          
        }
      }
    },

遍历对象一般是(name,value,index) in person方式,参数如下:

  • name:对象的属性名称,比如age、sex等
  • value:属性名称对应的属性值
  • index: 属性名称对应的下标
  • person:对象名称
  • key:同上

运行结果:

在这里插入图片描述

v-model

上面介绍的指令都是单向数据绑定,只能在js代码中触发改变data属性对象的值来刷新html。

v-model指令常用于表单 input、textarea 及 select 元素上实现双向数据绑定。所谓双向数据绑定是,在html元素交互改变的值会同步到js中data属性对象中,在js触发改变的值也会同步html上刷新,这就是双向数据绑定。

<input placeholder="输入名字" v-model="user_name">
<button @click="randomGeneration">随机名字 - {
   
   {user_name}}</button>


data() {
      return {
        user_name:'',
        lists: ['张三', '李四', '王老五', '王麻子'],
    },
    methods:{
      randomGeneration(){
        let randomPos=Math.floor(Math.random()*this.lists.length)
        this.user_name=this.lists[randomPos]
      }
    }

插槽

概念

插槽通俗的讲,就是将父组件内容添加到子组件中,在这个添加的过程中有一套规则标准。解决下面几个疑问就可以正确将父组件内容添加到子组件中:

  1. 内容需要插到子组件中哪个位置 – 内容存放的位置
  2. 内容在父组件哪个位置编写是合理的 – 内容的来源
  3. 父组件与子组件是如何数据交互的

内容需要插到子组件中哪个位置?

在子组件中通过slot指令定义声明内容存放的位置。如下:

子组件Child.vue

<template>
	
  <div>
    <slot>父组件内容存放的位置</slot>
  </div>
  
</template>

内容在父组件哪个位置编写是合理的?

父组件App.vue

 
<template>
  <div>
    <Child><template>插槽测试内容编写区</template></Child>
  </div>
</template>

<script type="text/ecmascript-6">
  import Child from './components/Child.vue'
  export default {
    name: 'App',
    components:{
      Child,
    },
 }
<script>    

注意:在父组件编写子组件插槽内容区域,需要用template元素包裹

运行后,会把父组件中的内容插槽测试内容编写区替换掉子组件中的父组件内容存放的位置文本。最终显示

在这里插入图片描述

具名插槽

上面示例是子组件只有单个插槽,如果多个插槽,父组件是如何区分的;其实这个很简单,就是在子组件中为每个插槽设置一个name属性来区分,name属性名称是唯一的。如下

子组件Child.vue

<div>
    <slot name="one"></slot>
    <slot name="two"></slot>
  </div>

父组件App.vue

<Child ><template v-slot:one>内容来源1</template></Child>
<Child ><template v-slot:two>内容来源2</template></Child>

在父组件中的template元素通过v-slot来绑定子组件的插槽属性名称,也可以简写如下

<Child ><template slot="one">内容来源1</template></Child>
<Child ><template slot="two">内容来源2</template></Child>

简单可以认为,通过name属性定义声明的插槽称之为具名插槽

插槽下父子组件的数据交互

前面解决了插槽内容的来源与存放位置这两大问题,只剩最后一步就是数据的交互。

父组件的编译作用域与子组件是不相通的,简单的说就是在父组件不能访问子组件的data属性内容。

正常情况下,父组件向子组件传递数据,会在子组件中定义声明props内容,同样地我们可以在子组件插槽slot元素中定义一个pros指令来绑定子组件的data属性内容。如下

子组件Child.vue

<template>
  
  <div>
    <div>
     <!--persons是插槽的prop属性-->
      <slot name="one" v-bind:persons="person"></slot>
      <!--age是插槽的prop属性-->
      <slot name="two" v-bind:age="maxAge"></slot>
    </div>
  </div>
</template>

<script>
  export default {
    name: "child",
    data() {
      return {
        person:{
          name:"张三"
        },
        maxAge:27
      }
    }
  }
</script>

注意:name属性相当于给每个插槽设置一个唯一属性

父组件App.vue

<Child >
      <template v-slot:one="user">
        <div>
          内容来源1 访问子组件数据 persons {
   
   {user.persons.name}}
        </div>
      </template>
</Child>
<Child >
    <template v-slot:two="user">
        内容来源2 访问子组件数据 age  {
   
   {user.age}}
    </template>
</Child>

v-slot:one插槽name属性后面添加别名(这个别名可以任意取名),然后用别名去调用插槽的prop属性,这样就可以访问子组件的数据。

在这里插入图片描述

  1. 子组件插槽的name属性名称,相对于在子组件自定义了一个绑定指令
  2. 子组件插槽的name名称别名,可以任意取名
  3. 子组件插槽的prop属性名称,相对于子组件插槽定义了public方法,提供外界来调用。

运行结果:

在这里插入图片描述

路由

路由的作用,是将单一Web页面实现成多视图页面的过程

官方推荐使用vue-router库,首先在项目的package.json中依赖路由插件:

 "dependencies": {
    "vue-router": "^3.0.1"
  },

路由命名

在Router实例中,在给routes 配置中给路由设置名称name,这个name是路由跳转的唯一标识符,可根据name来跳转哪个组件。

在这里插入图片描述

  1. path:路由的相对路径,如果只有/则是根路由,对应的组件会自动渲染到router-view的元素内
  2. name:路由的名称,可根据名称来跳转
  3. component:组件名称,先导入组件再声明

path路由路径,冒号:后面是用于路由跳转传参使用

    <!--路由跳转触发,router-link 最终会渲染成a标签-->
    <router-link :to="{name:'user'}">前往用户中心</router-link>
    <!--路由组件容器,目标组件所存放的位置,
    如果在应用第一个组件(App.vue)声明了router-view,则会自动加载根路由组件显示;
    其他情况都是用于渲染存放路由跳转的目标组件
    -->
    <router-view ></router-view>

路由跳转

触发路由跳转有两种方式,分别是:

  • 在html中使用router-link来渲染成a标签
  • 在js中编程代码的方式来跳转,比如push等api。

router-link路由跳转

router-link最终把它渲染给a标签,然后用router-link标签包裹触发元素,如下:

  <router-link :to="{name:'user'}">前往用户中心</router-link>
  • :to 是用于路由跳转的绑定指令
  • name是在命名的时候所定义的路由名称

上面是无参路由跳转,路由组件传参如下:

<router-link :to="{name:'user',params:{userId:123}}">前往用户中心</router-link>

通过params声明一个对象,用于初始化需要传参的参数,比如userId的属性,就是对应在路由路径path带有冒号:后面的名称

点击跳转最终的路径:

在这里插入图片描述

如果有多个参数需要传递,在注册命名路由处用/分割,如下:

path:'/user/:userId/:userName',

对应router-link触发点

<router-link :to="{name:'user',params:{userId:123,userName:'ABS'}}">前往用户中心</router-link>

编程式路由跳转

在vue内部可通过this.$router.push实现路由跳转,具体如下:

 <div @click="routePush">前往用户中心</div>
 
  routePush() {
        this.$router.push({
          name: 'user', params: {
            'userId': 987,
            "userName": 'AAS'
          }
        })
      }

路由获参

在目标路由组件接收参数,在vue的钩子函数mounted调用this.$route.params.来获取参数

    mounted() {
      this.userId= this.$route.params.userId
      this.userName= this.$route.params.userName
    }

总结

后面待续:

  • 网络请求
  • 状态管理

参考

  • https://www.jianshu.com/p/57f51bcdf1d4
  • https://zhuanlan.zhihu.com/p/114502325
  • https://router.vuejs.org/zh/guide/
  • https://cn.vuejs.org/v2/guide/

猜你喜欢

转载自blog.csdn.net/hzw2017/article/details/115029283
今日推荐