目录
使用vue-cli创建项目
通过指令:npm run serve运行
main.js代码:
// 入口js:创建Vue实例
import Vue from 'vue/dist/vue.esm.js'
import App from './App.vue'
new Vue({
el:'#app',
components:{
App
},
template: '<App/>'
})
HelloWorld.vue(组件vue)代码:
<template>
<div>
<p>{{msg}}</p>
</div>
</template>
<script>
export default { //配置对象(与Vue一致)
data(){ //data必须写函数
return {
msg:'hello!',
}
}
}
</script>
<style>
p{
color: aqua;
}
</style>
App.vue代码:
<template>
<div>
<img src="./assets/logo.png" alt="logo">
<!-- 3.使用组件标签 -->
<hello/>
</div>
</template>
<script>
//1.引入组件
import hello from './components/HelloWorld.vue'
export default {
//2.映射组件标签
components:{
hello
}
}
</script>
<style>
img{
width: 200px;
}
</style>
index.html代码:
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
碰到的问题
运行脚手架时报错:
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
解决方案:
在引用vue时直接写成如下写法
import Vue from 'vue/dist/vue.esm.js'
打包发布项目
通过指令:npm run build来打包项目
案例_评论
App.vue代码:
<template>
<div id="app">
<div>
<header class="site-header jumbotron">
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>请发表对Vue的评论</h1>
</div>
</div>
</div>
</header>
<div class="container">
<Add :addComment='addComment'/>
<List :comments="comments" :delComment='delComment'/>
</div>
</div>
</div>
</template>
<script>
import Add from "./components/Add.vue";
import List from "./components/List.vue";
export default {
data() {
return {
comments: [ //数据在哪个组件,更新数据的行为(方法)就应该定义在哪个组件
{ name: "Bob", content: "Vue还不错" },
{ name: "Mike", content: "Vue很简单" },
{ name: "Chen", content: "Vue很难" },
],
};
},
methods:{
addComment(comment){
this.comments.unshift(comment);
},
delComment(index){
this.comments.splice(index,1);
}
},
components: {
Add,
List,
},
};
</script>
<style>
</style>
List.vue代码:
<template>
<div class="col-md-8">
<h3 class="reply">评论回复:</h3>
<h2 v-show="comments.length==0">暂无评论,点击左侧添加评论!!!</h2>
<ul class="list-group">
<Item
v-for="(comment, index) in comments"
:key="index"
:comment='comment'
:delComment='delComment'
:index='index'
/>
</ul>
</div>
</template>
<script>
import Item from "./Item.vue";
export default {
//声明接收属性:这个属性就会成为组件对象的属性
props: ["comments","delComment"], //这种方式只指定了属性名
components: {
Item,
},
};
</script>
<style>
.reply {
margin-top: 0px;
}
li {
transition: 0.5s;
overflow: hidden;
}
.handle {
width: 40px;
border: 1px solid #ccc;
background: #fff;
position: absolute;
right: 10px;
top: 1px;
text-align: center;
}
.handle a {
display: block;
text-decoration: none;
}
.list-group-item .centence {
padding: 0px 50px;
}
.user {
font-size: 22px;
}
</style>
Add.vue代码:
<template>
<div class="col-md-4">
<form class="form-horizontal">
<div class="form-group">
<label>用户名</label>
<input
type="text"
class="form-control"
placeholder="用户名"
v-model="name"
>
</div>
<div class="form-group">
<label>评论内容</label>
<textarea
class="form-control"
rows="6"
placeholder="评论内容"
v-model="content"
></textarea>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button
type="button"
class="btn btn-default pull-right"
@click="add"
>提交</button>
</div>
</div>
</form>
</div>
</template>
<script>
export default {
props:{
addComment:{ //指定属性名/属性值的类型/必要性
type:Function,
required:true
}
},
data(){
return{
name:'',
content:''
}
},
methods:{
add(){
//1.检查输入的合法性
const name=this.name.trim();
const content=this.content.trim();
if(!name || !content){
alert('姓名或内容不能为空');
return;
}
//2.根据输入的数据封装成一个comment对象
const comment={name,content};
//3.添加到comments中
this.addComment(comment);
//4.清除输入
this.name='';this.content='';
}
}
};
</script>
<style>
</style>
Item.vue代码:
<template>
<div>
<li class="list-group-item">
<div class="handle">
<a href="javascript:;" @click="del">删除</a>
</div>
<p class="user"><span>{{comment.name}}</span><span>说:</span></p>
<p class="centence">{{comment.content}}</p>
</li>
</div>
</template>
<script>
export default {
props: {
//指定属性名和属性值的类型
comment: Object,
delComment:Function,
index:Number
},
methods:{
del(){
const {comment,index,delComment}=this;
if(window.confirm('确定删除'+comment.name+'的评论吗?')){
delComment(index);
}
}
}
};
</script>
<style>
</style>
组件间通信
通过在标签处添加属性可实现将data中的数据传入到对应的组件中,然后在相应的组件的js中通过props声明接受属性。
组件间通信的基本原则:
- 不要在子组件中直接修改父组件的状态数据
- 数据在哪,更新数据的行为(方法)就定义在哪
props通信
使用组件标签:
<my-component name='tom' :age='3' :set-name='setName'></my-component>
定义Mycomment时:
- 在组件内声明所有的props
- 方式一:只指定名称
props: ['name', 'age', 'setName']
- 方式二:指定名称和类型
props: {
name: String,
age: Number,
setNmae: Function
}
- 方式三:指定名称/类型/必要性/默认值
props: {
name: {type: String, required: true, default:xxx},
}
注意:
- 此方式用于父组件向子组件传递数据
- 所有标签属性都会成为组件对象的属性,模板页面可以直接引用
- 如果需要向非直接子后代传递数据必须逐层传递
- 兄弟组件也不能直接props通信,必须借助父组件才可以
案例_todoList
App.vue代码:
<template>
<div class="todo-container">
<div class="todo-wrap">
<todoHeader :addTodo="addTodo"/>
<todoList :todos='todos' :delTodo='delTodo'/>
<todoFooter :todos='todos' :delCom='delCom' :selectAll='selectAll'/>
</div>
</div>
</template>
<script>
import todoHeader from './components/todoHeader.vue'
import todoList from './components/todoList.vue'
import todoFooter from './components/todoFooter.vue'
export default {
data(){
return {
//从localStroage读取todos
todos:JSON.parse(window.localStorage.getItem('todos_key')||'[]')
}
},
watch:{ //监视
todos:{
deep:true, //深度监视
handler:function(newValue){
//将todo最新值的JSON数据,保存到localStroage
window.localStorage.setItem('todos_key',JSON.stringify(newValue));
}
}
},
components:{
todoHeader,todoList,todoFooter
},
methods:{
addTodo(todo){
this.todos.unshift(todo);
},
delTodo(index){
this.todos.splice(index,1);
},
delCom(){
this.todos=this.todos.filter(todo=>!todo.complete);
},
selectAll(check){
this.todos.forEach(todo=>todo.complete=check);
}
}
};
</script>
<style>
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
</style>
todoHeader.vue代码:
<template>
<div class="todo-header">
<input
type="text"
placeholder="请输入你的任务名称,按回车键确认"
@keyup.enter="add"
v-model="title"
/>
</div>
</template>
<script>
export default {
props:{
addTodo:Function
},
data(){
return {
title:''
}
},
methods:{
add(){
//1.检查输入的合法性
const title=this.title.trim();
if(!title){
alert("请输入!");
retrun;
}
//2.根据输入生成一个todo对象
const todo={
title,
complete:false
};
//3.添加到todos
this.addTodo(todo);
//4.清除输入
this.title='';
}
}
};
</script>
<style>
.todo-header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus {
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>
todoList.vue代码:
<template>
<ul class="todo-main">
<todoItem v-for="(todo, index) in todos" :key="index" :todo='todo' :index='index' :delTodo='delTodo'/>
</ul>
</template>
<script>
import todoItem from './todoItem.vue'
export default {
props:{
todos:Array,
delTodo:Function
},
components:{
todoItem
}
};
</script>
<style>
.todo-main {
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
</style>
todoItem.vue代码:
<template>
<li @mouseenter="show(true)" @mouseleave="show(false)" :style="{background:bgColor}">
<label>
<input type="checkbox" v-model="todo.complete"/>
<span>{{todo.title}}</span>
</label>
<button
class="btn btn-danger"
v-show="isShow"
@click="del"
>删除</button>
</li>
</template>
<script>
export default {
props:{
todo:Object,
index:Number,
delTodo:Function
},
data(){
return {
bgColor:'white',
isShow:false,
}
},
methods:{
show(isEnter){
if(isEnter){
this.bgColor='gray';
this.isShow=true;
}
else {
this.bgColor='white';
this.isShow=false;
}
},
del(){
const {todo,index,delTodo}=this;
if(window.confirm('确认删除'+todo.title+'吗?')){
delTodo(index);
}
}
}
}
</script>
<style>
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
</style>
todoFooter.vue代码:
<template>
<div class="todo-footer">
<label>
<input type="checkbox" v-model="isCheck"/>
</label>
<span>
<span>已完成{{completeCount}}</span> / 全部{{todos.length}}
</span>
<button class="btn btn-danger" @click="delCom()" v-show="completeCount">清除已完成任务</button>
</div>
</template>
<script>
export default {
props:{
todos:Array,
delCom:Function,
selectAll:Function
},
computed:{
completeCount(){
var count=0;
const {todos}=this;
for(var i=0;i<todos.length;i++){
if(todos[i].complete==true) count++;
}
return count;
},
isCheck:{
get(){
return this.completeCount==this.todos.length && this.todos.length>0;
},
set(value){
this.selectAll(value);
}
}
}
};
</script>
<style>
.todo-footer {
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label {
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input {
position: relative;
top: -1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button {
float: right;
margin-top: 5px;
}
</style>
存储数据
通过对todos数组进行深度监视,在localStroage中读写数据:
-
从localStroage读取todos
data(){
return {
//从localStroage读取todos
todos:JSON.parse(window.localStorage.getItem('todos_key')||'[]')
}
}
-
将todo最新值的JSON数据,保存到localStroage
watch:{ //监视
todos:{
deep:true, //深度监视
handler:function(newValue){
//将todo最新值的JSON数据,保存到localStroage
window.localStorage.setItem('todos_key',JSON.stringify(newValue));
}
}
}