一、小案例(评论区)
1、流程
(1)分析静态页面。(vue项目创建参考https://www.cnblogs.com/l-y-h/p/11241503.html)
(2)拆分静态页面,变成一个个组件。
(3)对组件编码,生成动态页面。
2、静态页面
参考来源:https://www.bilibili.com/video/av49099807/?p=22&t=1223
【举例:】 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <!--此处如果bootstrap选用 4.3.1的版本,样式会无效(没去研究)--> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/js/bootstrap.js"></script> <title>vue_demo</title> </head> <body> <div id="app"> <div> <!--头部--> <header class="site-header jumbotron"> <div class="container"> <div class="row"> <div class="col-xs-12"> <h1>欢迎来到吐槽大厅</h1> </div> </div> </div> </header> <!--主体部分--> <!--bootstrap将页面分为12格,此处拆分为左4格,右8格--> <div class="container"> <div class="col-md-4"> <form action="form-horizontal"> <div class="form-group"> <label>用户名</label> <input type="text" class="form-control" placeholder="用户名"> </div> <div class="form-group"> <label>吐槽内容</label> <textarea type="text" class="form-control" placeholder="吐槽内容"></textarea> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="button" class="btn btn-default pull-right">提交</button> </div> </div> </form> </div> <!--md4 for Add end --> <div class="col-md-8"> <h3 class="reply">吐槽回复:</h3> <h2>暂无吐槽,点击左侧添加吐槽吧!</h2> <ul class="list-group"> <li class="list-group-item"> <div class="handle col-sm-offset-2 col-sm-10"> <a class="pull-right">删除</a> </div> <p class="user"><span>Tom</span><span>说:</span></p> </li> <li class="list-group-item"> <div class="handle col-sm-offset-2 col-sm-10"> <a class="pull-right">删除</a> </div> <p class="user"><span>Tom</span><span>说:</span></p> </li> </ul> </div> <!--md8 for List end --> </div> </div> </div> <!--app --> </body> </html>
页面截图:
3、拆分静态页面,
拆分静态页面,使其变成一个个静态组件。
Step1:是一个大的组件(App),里面包含各种组件。
Step2:页面内容可以拆分成 提交吐槽组件(Comment),吐槽回复组件(Comments)。
Step3:吐槽回复组件里面 可以对 每一条吐槽进行拆分,即每个吐槽为一个组件(Item)。
文件结构如下:
【主要文件与文件夹:】 index.html 主页面,所有组件操作均为其服务,在此处引入css、js文件 main.js vue入口文件,从此处启动vue App.vue App.vue组件,项目的入口组件 components 里面保存各个小组件 【index.html】 <!DOCTYPE html> <html lang="en"> <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"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <!--所有组件都是为index.html服务的,所以在此处引入css、js文件--> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <title>vuedemo</title> </head> <body> <noscript> <strong>We're sorry but vuedemo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html> 【main.js】 import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app') 【App.vue】 <template> <div> <!--头部--> <header class="site-header jumbotron"> <div class="container"> <div class="row"> <div class="col-xs-12"> <h1>欢迎来到吐槽大厅</h1> </div> </div> </div> </header> <!--主体部分--> <!--bootstrap将页面分为12格,此处拆分为左4格,右8格--> <div class="container"> <!--使用各组件--> <Comment></Comment> <Comments></Comments> </div> </div> <!--App --> </template> <script> // 引入各组件 import Comment from './components/Comment.vue' import Comments from './components/Comments.vue' export default { name: 'app', // 注册各组件 components: { Comment, Comments } } </script> <style> </style> 【Comment.vue】 <template> <div class="col-md-4"> <form action="form-horizontal"> <div class="form-group"> <label>用户名</label> <input type="text" class="form-control" placeholder="用户名"> </div> <div class="form-group"> <label>吐槽内容</label> <textarea type="text" class="form-control" placeholder="吐槽内容"></textarea> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="button" class="btn btn-default pull-right">提交</button> </div> </div> </form> </div> <!--Comment --> </template> <script> export default{ name: 'comment' } </script> <style> </style> 【Comments.vue】 <template> <div class="col-md-8"> <h3 class="reply">吐槽回复:</h3> <h2>暂无吐槽,点击左侧添加吐槽吧!</h2> <ul class="list-group"> <Item></Item> </ul> </div> <!--md8 for List end --> </template> <script> import Item from './Item.vue' export default{ name: 'comments', components: { Item } } </script> <style> </style> 【Item.vue】 <template> <!--注意,需要使用div包裹,否则会报错--> <div> <li class="list-group-item"> <div class="handle col-sm-offset-2 col-sm-10"> <a class="pull-right">删除</a> </div> <p class="user"><span>Tom</span><span>说:</span></p> </li> <li class="list-group-item"> <div class="handle col-sm-offset-2 col-sm-10"> <a class="pull-right">删除</a> </div> <p class="user"><span>Tom</span><span>说:</span></p> </li> </div> </template> <script> export default{ name: 'item' } </script> <style> </style>
拆分后效果与原静态页面一致。
4、组件间值的传递(组件间通信)
静态页面上吐槽区的内容不会是写好的,是动态生成的,那么如何生成,就涉及到组件间的值的传递。通过props 来声明属性,使用data来传递数据(属性值),使用 v-bind 绑定属性。
【对上面代码进行修改】 App.vue 获取数据,将数据往吐槽区(Comments.vue)传 Comments.vue 接收App.vue传来的数据,将每条数据往Item.vue传 Item.vue 接收Comments.vue传来的数据并显示 要是一眼看不出来,可以下载个Bcompare软件,自行比较一下代码间的区别。 【App.vue】 <template> <div> <!--头部--> <header class="site-header jumbotron"> <div class="container"> <div class="row"> <div class="col-xs-12"> <h1>欢迎来到吐槽大厅</h1> </div> </div> </div> </header> <!--主体部分--> <!--bootstrap将页面分为12格,此处拆分为左4格,右8格--> <div class="container"> <!--使用各组件--> <Comment></Comment> <!--需使用v-bind绑定属性--> <Comments :contents="contents"></Comments> </div> </div> <!--App --> </template> <script> // 引入各组件 import Comment from './components/Comment.vue' import Comments from './components/Comments.vue' export default { name: 'app', // 注册各组件 components: { Comment, Comments }, // 传递数据 data(){ return { contents:[ {name: 'tom', content: '妈妈,我想吃烤山药'}, {name: 'jarry', content: '吃,吃大块的'}, {name: 'jarry', content: '两块够不'}, {name: 'tom', content: '够了,妈妈真好,谢谢妈妈'}, ] } } } </script> <style> </style> 【Comments.vue】 <template> <div class="col-md-8"> <h3 class="reply">吐槽回复:</h3> <ul class="list-group"> <Item v-for="(content, index) in contents" :key="index" :content="content"></Item> </ul> </div> <!--md8 for List end --> </template> <script> import Item from './Item.vue' export default{ name: 'comments', // 声明接收属性,此属性可以在该组件中使用 props: ['contents'], // 只指定属性名 // 注册组件 components: { Item } } </script> <style> </style> 【Item.vue】 <template> <!--注意,需要使用div包裹,否则会报错--> <div> <li class="list-group-item"> <div class="handle col-sm-offset-2 col-sm-10"> <a class="pull-right">删除</a> </div> <p class="user"><span style="font-size: 18px;">{{content.name}}</span><span style="font-size: 18px;">说:</span>{{content.content}}</p> </li> </div> </template> <script> export default{ name: 'item', props: { // 指定属性名以及属性值的类型 content : Object } } </script> <style> </style>
效果如下图:
5、动态交互--添加
实现添加吐槽操作。
使用v-on 绑定事件,使用v-model 实现数据的双向绑定,方法也可以使用 v-bind 绑定 并进行组件通信。
【对上面代码进行修改】 App.vue 定义增加吐槽的方法,并作为属性传递给Comment.vue组件 Comment.vue 接收属性,并定义添加数据的事件 【App.vue】 <template> <div> <!--头部--> <header class="site-header jumbotron"> <div class="container"> <div class="row"> <div class="col-xs-12"> <h1>欢迎来到吐槽大厅</h1> </div> </div> </div> </header> <!--主体部分--> <!--bootstrap将页面分为12格,此处拆分为左4格,右8格--> <div class="container"> <!--使用各组件--> <Comment :addComment="addComment"></Comment> <!--需使用v-bind绑定属性--> <Comments :contents="contents"></Comments> </div> </div> <!--App --> </template> <script> // 引入各组件 import Comment from './components/Comment.vue' import Comments from './components/Comments.vue' export default { name: 'app', // 注册各组件 components: { Comment, Comments }, // 传递数据 data(){ return { contents:[ {name: 'tom', content: '妈妈,我想吃烤山药'}, {name: 'jarry', content: '吃,吃大块的'}, {name: 'jarry', content: '两块够不'}, {name: 'tom', content: '够了,妈妈真好,谢谢妈妈'}, ] } }, // 操作数据的方法 methods: { addComment(comment){ // 在数组头部插入数据 this.contents.unshift(comment); } } } </script> <style> </style> 【Comment.vue】 <template> <div class="col-md-4"> <form action="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 type="text" class="form-control" 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> <!--Comment --> </template> <script> export default{ name: 'comment', data(){ return { name : '', content : '' } }, props:{ // 定义属性类型、属性值类型、必须性 addComment: { type: Function, required: true } }, methods: { add(){ // step1:进行合法性检验 const name = this.name.trim(); const content = this.content.trim(); if(!name || !content){ alert("输入内容不能为空"); return; } // step2:将name,content封装成一个comment(吐槽)对象 const comment = {name, content}; console.log(comment); // step3: 将comment 加入到 comments(吐槽区) this.addComment(comment); // step4:清空输入框 this.name = ''; this.content = ''; } } } </script> <style> </style>
效果:
6、动态交互--删除
进行删除操作。
类似于添加操作。
【对上面文件进行修改】 App.vue 定义删除数据的方法,并将其作为属性传递给Comments.vue Comments.vue 作为一个中间的组件,传递index以及删除方法 Item.vue 接收Comments.vue传递的属性,并定义删除事件 【App.vue】 <template> <div> <!--头部--> <header class="site-header jumbotron"> <div class="container"> <div class="row"> <div class="col-xs-12"> <h1>欢迎来到吐槽大厅</h1> </div> </div> </div> </header> <!--主体部分--> <!--bootstrap将页面分为12格,此处拆分为左4格,右8格--> <div class="container"> <!--使用各组件--> <Comment :addComment="addComment"></Comment> <!--需使用v-bind绑定属性--> <Comments :contents="contents" :deleteComment="deleteComment"></Comments> </div> </div> <!--App --> </template> <script> // 引入各组件 import Comment from './components/Comment.vue' import Comments from './components/Comments.vue' export default { name: 'app', // 注册各组件 components: { Comment, Comments }, // 传递数据 data(){ return { contents:[ {name: 'tom', content: '妈妈,我想吃烤山药'}, {name: 'jarry', content: '吃,吃大块的'}, {name: 'jarry', content: '两块够不'}, {name: 'tom', content: '够了,妈妈真好,谢谢妈妈'}, ] } }, // 操作数据的方法 methods: { addComment(comment){ // 在数组头部插入数据 this.contents.unshift(comment); }, deleteComment(index){ // 删除指定下标的数据 this.contents.splice(index, 1); } } } </script> <style> </style> 【Comments.vue】 <template> <div class="col-md-8"> <h3 class="reply">吐槽回复:</h3> <h3 v-show="contents.length === 0">暂无吐槽,点击左侧提交吐槽!!!</h3> <ul class="list-group"> <Item v-for="(content, index) in contents" :key="index" :content="content" :deleteComment="deleteComment" :index="index"></Item> </ul> </div> <!--md8 for List end --> </template> <script> import Item from './Item.vue' export default { name: 'comments', // 声明接收属性,此属性可以在该组件中使用 props: ['contents', 'deleteComment'], // 只指定属性名 // 注册组件 components: { Item } } </script> <style> </style> 【Item.vue】 <template> <!--注意,需要使用div包裹,否则会报错--> <div> <li class="list-group-item"> <div class="handle col-sm-offset-2 col-sm-10"> <a class="pull-right" @click="deleteItem">删除</a> </div> <p class="user"><span style="font-size: 18px;">{{content.name}}</span><span style="font-size: 18px;">说:</span>{{content.content}}</p> </li> </div> </template> <script> export default{ name: 'item', props: { // 指定属性名以及属性值的类型 content : Object, deleteComment : Function, index : Number }, methods: { deleteItem() { const {content, deleteComment, index} = this; // 使用 反引号 + ${}, ES6模板字符串 if(window.confirm(`确定删除${content.name}的评论吗?`)){ deleteComment(index); } } } } </script> <style> </style>
运行结果:
7、完整代码
(1)项目结构以及修改的文件
(2)代码
【index.html】 <!DOCTYPE html> <html lang="en"> <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"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <!--所有组件都是为index.html服务的,所以在此处引入css、js文件--> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <title>vuedemo</title> </head> <body> <noscript> <strong>We're sorry but vuedemo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html> 【main.js】 import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app') 【App.vue】 <template> <div> <!--头部--> <header class="site-header jumbotron"> <div class="container"> <div class="row"> <div class="col-xs-12"> <h1>欢迎来到吐槽大厅</h1> </div> </div> </div> </header> <!--主体部分--> <!--bootstrap将页面分为12格,此处拆分为左4格,右8格--> <div class="container"> <!--使用各组件--> <Comment :addComment="addComment"></Comment> <!--需使用v-bind绑定属性--> <Comments :contents="contents" :deleteComment="deleteComment"></Comments> </div> </div> <!--App --> </template> <script> // 引入各组件 import Comment from './components/Comment.vue' import Comments from './components/Comments.vue' export default { name: 'app', // 注册各组件 components: { Comment, Comments }, // 传递数据 data(){ return { contents:[ {name: 'tom', content: '妈妈,我想吃烤山药'}, {name: 'jarry', content: '吃,吃大块的'}, {name: 'jarry', content: '两块够不'}, {name: 'tom', content: '够了,妈妈真好,谢谢妈妈'}, ] } }, // 操作数据的方法 methods: { addComment(comment){ // 在数组头部插入数据 this.contents.unshift(comment); }, deleteComment(index){ // 删除指定下标的数据 this.contents.splice(index, 1); } } } </script> <style> </style> 【Comment.vue】 <template> <div class="col-md-4"> <form action="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 type="text" class="form-control" 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> <!--Comment --> </template> <script> export default{ name: 'comment', data(){ return { name : '', content : '' } }, props:{ // 定义属性类型、属性值类型、必须性 addComment: { type: Function, required: true } }, methods: { add(){ // step1:进行合法性检验 const name = this.name.trim(); const content = this.content.trim(); if(!name || !content){ alert("输入内容不能为空"); return; } // step2:将name,content封装成一个comment(吐槽)对象 const comment = {name, content}; console.log(comment); // step3: 将comment 加入到 comments(吐槽区) this.addComment(comment); // step4:清空输入框 this.name = ''; this.content = ''; } } } </script> <style> </style> 【Comments.vue】 <template> <div class="col-md-8"> <h3 class="reply">吐槽回复:</h3> <h3 v-show="contents.length === 0">暂无吐槽,点击左侧提交吐槽!!!</h3> <ul class="list-group"> <Item v-for="(content, index) in contents" :key="index" :content="content" :deleteComment="deleteComment" :index="index"></Item> </ul> </div> <!--md8 for List end --> </template> <script> import Item from './Item.vue' export default { name: 'comments', // 声明接收属性,此属性可以在该组件中使用 props: ['contents', 'deleteComment'], // 只指定属性名 // 注册组件 components: { Item } } </script> <style> </style> 【Item.vue】 <template> <!--注意,需要使用div包裹,否则会报错--> <div> <li class="list-group-item"> <div class="handle col-sm-offset-2 col-sm-10"> <a class="pull-right" @click="deleteItem">删除</a> </div> <p class="user"><span style="font-size: 18px;">{{content.name}}</span><span style="font-size: 18px;">说:</span>{{content.content}}</p> </li> </div> </template> <script> export default{ name: 'item', props: { // 指定属性名以及属性值的类型 content : Object, deleteComment : Function, index : Number }, methods: { deleteItem() { const {content, deleteComment, index} = this; // 使用 反引号 + ${}, ES6模板字符串 if(window.confirm(`确定删除${content.name}的评论吗?`)){ deleteComment(index); } } } } </script> <style> </style>
运行结果此处不再重复截图,与上述截图相同。