文章目录
- 一、前言
- 二、Vue.js安装
- 三、初始化Vue项目
- 四、项目目录解析
- 五、Vue核心指令
- 1、插值表达式
- 2、v-text
- 3、v-html
- 4、v-on
- 5、计数器实战
- 6、v-show
- 7、v-if
- 8、v-bind
- 9、v-for
- 10、v-model
- 六、实战小项目
- 1、记事本
- 2、天气预报
- 3、音乐播放器
一、前言
Vue
(读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。
Vue通过MVVM模式,能够实现视图与模型的双向绑定。
简单来说,就是数据变化的时候, 页面会自动刷新, 页面变化的时候,数据也会自动变化.
二、Vue.js安装
NPM方法(推荐使用)
-
安装node.js
从node.js官网下载并安装node,安装过程很简单,一直点下一步就ok了,输入
node -v
命令,查看node的版本 -
npm -v
-
安装cnpm
在命令行中输入npm install -g cnpm --registry=http://registry.npm.taobao.org
-
安装vue-cli2 脚手架构建工具(必须在全局中进行安装)
在命令行中运行命令npm install -g vue-cli
,然后等待安装完成。
是否安装成功:vue -V
三、初始化Vue项目
命令:vue init webpack firstApp
输入命令后,会询问我们几个简单的选项,我们根据自己的需要进行填写就可以了。
- Project name :项目名称 ,如果不需要更改直接回车就可以了。注意:这里不能使用大写,所以我把名称改成了vueclitest
- Project description:项目描述,默认为A Vue.js project,直接回车,不用编写。
- Author:作者,如果你有配置git的作者,他会读取。
- Install vue-router? 是否安装vue的路由插件,我们这里需要安装,所以选择Y
- Use ESLint to lint your code? 是否用ESLint来限制你的代码错误和风格。我们这里不需要输入n(建议),如果你是大型团队开发,最好是进行配置。
- setup unit tests with Karma + Mocha? 是否需要安装单元测试工具Karma+Mocha,我们这里不需要,所以输入n。
- Setup e2e tests with Nightwatch?是否安装e2e来进行用户行为模拟测试,我们这里不需要,所以输入n
四、项目目录解析
-
build:最终发布的代码的存放位置。
-
config:配置路径、端口号等一些信息,我们刚开始学习的时候选择默认配置。
-
node_modules:npm 加载的项目所需要的各种依赖模块。
-
src:这里是我们开发的主要目录(源码),基本上要做的事情都在这个目录里面,里面包含了几个目录及文件:
-
assets:放置一些图片(会根据图片大小分类进行base64命名还是其他方式命名),如logo等
-
components:目录里放的是一个个的组件文件
-
router/index.js:配置路由的地方
-
-
App.vue:项目入口组件(跟组件),我们也可以将组件写这里,而不使用components目录。主要作用就是将我们自己定义的组件通过它与页面建立联系进行渲染,这里面的必不可少。
-
main.js :项目的核心文件(整个项目的入口js)引入依赖包、默认页面样式等(项目运行后会在index.html中形成一个app.js文件)。
-
static:静态资源目录(会原分不动的对文件进行处理),如图片、字体等。
-
test:初始测试目录,可删除
-
.XXXX文件:配置文件。
-
index.html:html单页面的入口页面,可以添加一些meta信息或者同统计代码啥的或页面的重置样式等。
-
package.json:项目配置信息文件/所依赖的开发包的版本信息及所依赖的插件信息。(大概版本)
-
package-lock.json:项目配置信息文件/所依赖的开发包的版本信息及所依赖的插件信息。(具体版本)
-
README.md:项目的说明文件。
-
webpack.config.js:webpack的配置文件,例:把.vue的文件打包成浏览器能读懂的文件。
-
.babelrc:是检测es6语法的配置文件,例:适配哪些浏览器的限制
-
.gitignore:上传到服务器忽略哪些文件的配置(比如模拟本地数据mock不让他在get提交/打包上线的时候忽略不使用可在这里配置)
-
.postcssrc.js:前缀的配置 (css转化的配置)
-
.editorconfig:对代码进行规范,例:root是否进行检测,代码尾部是否换行,缩行前面几个空格…(建议定义这个规范)
-
.eslintrc.js:配置eslint语法规则(在这里面的rules属性中配置让哪个语法规则失效)
-
.eslintignore:忽略eslint对项目某些文件的语法规则的检查
若拿到别人的项目或从gethub上下载的项目第一步就是要在项目中cnpm install
;下载项目所依赖的插件,然后npm run dev (npm run start)
运行项目
五、Vue核心指令
Vue导入
<!-- 开发环境版本,包含了有帮助的命令行警告 --><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
或
<!-- 生产环境版本,优化了尺寸和速度 --><script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
1、插值表达式
指令:{{ }}
<div id="app"> <!-- 插值表达式获取vue中的msg信息 --> <p>{{ msg }}</p> {{message}} <hr> {{student}} <hr> {{student.name}} <hr> {{school[0]}} <hr> {{school}} </div> <script> var vm = new Vue({ el: '#app', data: { message: " Hello,Vue!", student:{ name:"步尔斯特", age:18 }, school:["1","2"] }, })</script>
2、v-text
设置标签的文本值(textContent)
<body> <div id="app"> <h2 v-text="message+'very good'"></h2> {{message+"very good"}} </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "hello,步尔斯特." } }) </script></body>
3、v-html
设置标签的innerHTML,内容中有html结构会被解析为标签
<body> <div id="app"> <b v-html="con"></a> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "hello,步尔斯特.", con:"<a href='http://#'>步尔斯特</a>" } }) </script></body>
4、v-on
为元素绑定事件
<body> <div id="app"> <input type="button" value="单击" v-on:click="clicktest"> <input type="button" value="鼠标" v-on:mouseenter="clicktest"> <input type="button" value="双击" v-on:dblclick="clicktest"> <input type="button" value="单击简写" @click="clicktest"> <hr> <h2 @click="clicktest">{{message}}</h2> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "步尔斯特", count: "0" }, methods: { clicktest: function () { this.message = "已经被修改" + this.count++ } } }) </script></body>
传递自定义参数,事件修饰符
<input type="button" value="单击简写" @click="clicktest('Java')"> ... methods: { clicktest: function (p1) { this.message = p1 + "已经被修改" + this.count++ }
5、计数器实战
<body> <div id="app"> <button @click="sub">-</button> <span>{{count}}</span> <button @click="add">+</button> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "步尔斯特", count: "0" }, methods: { add: function () { this.count++ }, sub:function(){ this.count-- } } }) </script></body>
6、v-show
根据表达式的真假,切换元素的显示和隐藏
<body> <div id="app"> <button @click="changee">步尔斯特</button> <h4 v-show="isShow">{{message}}</h4> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "步尔斯特", isShow: false }, methods: { changee: function () { this.isShow = !this.isShow } } }) </script></body>
7、v-if
根据表达式的真假,切换元素的显示和隐藏(操作dom元素)
<body> <div id="app"> <button @click="changee">步尔斯特</button> <h4 v-if="isIf">{{message}}</h4> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "步尔斯特", isIf: false }, methods: { changee: function () { this.isIf = !this.isIf } } }) </script></body>
8、v-bind
设置元素属性
<body> <div id="app"> <button @click="changee">步尔斯特</button> <img v-if="isIf" v-bind:src="mysrc"> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { isIf:false, mysrc: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic%2Ff7%2F80%2F97%2Ff7809705cbe5c0fc580e401270522a0a.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1650790832&t=74454e75c55e6a3b59f6aa3be6584681" }, methods: { changee: function () { this.isIf = !this.isIf } } }) </script></body>
9、v-for
根据数据生成列表结构,经常和数组结合使用
<body> <div id="app"> <ul> <li v-for="(item,index) in arr" :title="item"> {{index}} -> {{item}} </li> </ul> <ul> <li v-for="(item,index) in objArr" :title="item"> {{item.name}} </li> </ul> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { arr: [1, 2, 3, 4, 5], objArr: [{ name: "jack" }, { name: "zhangsan" }] } }) </script></body>
10、v-model
获取和设置表单元素的值
<body> <div id="app"> <input type="text" v-model="message" @keyup.enter="getM"> <h2>{{message}}</h2> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "步尔斯特" }, methods: { getM: function () { alert(this.message) } } }) </script></body>
六、实战小项目
1、记事本
可动态增加、删除、显示总数、一键清除、隐藏
html
<html><head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <title>步尔斯特记事本</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <meta name="robots" content="noindex, nofollow" /> <meta name="googlebot" content="noindex, nofollow" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" type="text/css" href="./css/index.css" /></head><body> <!-- 主体区域 --> <section id="todoapp"> <!-- 输入框 --> <header class="header"> <h1>步尔斯特记事本</h1> <input v-model="inputValue" @keyup.enter="add" autofocus="autofocus" autocomplete="off" placeholder="请输入任务" class="new-todo" /> </header> <!-- 列表区域 --> <section class="main"> <ul class="todo-list"> <li class="todo" v-for="(item,index) in list"> <div class="view"> <span class="index">{{ index+1 }}.</span> <label>{{ item }}</label> <button class="destroy" @click="remove(index)"></button> </div> </li> </ul> </section> <!-- 统计和清空 --> <footer class="footer" v-show="list.length!=0"> <span class="todo-count" v-if="list.length!=0"> <strong>{{ list.length }}</strong> items left </span> <button v-show="list.length!=0" class="clear-completed" @click="clear"> Clear </button> </footer> </section> <!-- 底部 --> <footer class="info"> <p> <a href="https://blog.csdn.net/CSDN_SAVIOR"><button value="步尔斯特"></button> </a> </p> </footer> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> var app = new Vue({ el: "#todoapp", data: { list: ["云计算", "云原生", "Vue"], inputValue: "csdn热榜" }, methods: { add: function () { this.list.push(this.inputValue); }, remove: function (index) { console.log("删除"); console.log(index); this.list.splice(index, 1); }, clear: function () { this.list = []; } }, }) </script></body></html>
css
html,body { margin: 0; padding: 0;}body { background: #fff;}button { margin: 0; padding: 0; border: 0; background: none; font-size: 100%; vertical-align: baseline; font-family: inherit; font-weight: inherit; color: inherit; -webkit-appearance: none; appearance: none; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}body { font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif; line-height: 1.4em; background: #f5f5f5; color: #4d4d4d; min-width: 230px; max-width: 550px; margin: 0 auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-weight: 300;}:focus { outline: 0;}.hidden { display: none;}#todoapp { background: #fff; margin: 180px 0 40px 0; position: relative; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);}#todoapp input::-webkit-input-placeholder { font-style: italic; font-weight: 300; color: #e6e6e6;}#todoapp input::-moz-placeholder { font-style: italic; font-weight: 300; color: #e6e6e6;}#todoapp input::input-placeholder { font-style: italic; font-weight: 300; color: gray;}#todoapp h1 { position: absolute; top: -160px; width: 100%; font-size: 60px; font-weight: 100; text-align: center; color: rgba(175, 47, 47, .8); -webkit-text-rendering: optimizeLegibility; -moz-text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;}.new-todo,.edit { position: relative; margin: 0; width: 100%; font-size: 24px; font-family: inherit; font-weight: inherit; line-height: 1.4em; border: 0; color: inherit; padding: 6px; border: 1px solid #999; box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); box-sizing: border-box; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}.new-todo { padding: 16px; border: none; background: rgba(0, 0, 0, 0.003); box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);}.main { position: relative; z-index: 2; border-top: 1px solid #e6e6e6;}.toggle-all { width: 1px; height: 1px; border: none; opacity: 0; position: absolute; right: 100%; bottom: 100%;}.toggle-all + label { width: 60px; height: 34px; font-size: 0; position: absolute; top: -52px; left: -13px; -webkit-transform: rotate(90deg); transform: rotate(90deg);}.toggle-all + label:before { content: "❯"; font-size: 22px; color: #e6e6e6; padding: 10px 27px 10px 27px;}.toggle-all:checked + label:before { color: #737373;}.todo-list { margin: 0; padding: 0; list-style: none; max-height: 420px; overflow: auto;}.todo-list li { position: relative; font-size: 24px; border-bottom: 1px solid #ededed; height: 60px; box-sizing: border-box;}.todo-list li:last-child { border-bottom: none;}.todo-list .view .index { position: absolute; color: gray; left: 10px; top: 20px; font-size: 16px;}.todo-list li .toggle { text-align: center; width: 40px; height: auto; position: absolute; top: 0; bottom: 0; margin: auto 0; border: none; -webkit-appearance: none; appearance: none;}.todo-list li .toggle { opacity: 0;}.todo-list li .toggle + label { background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: center left;}.todo-list li .toggle:checked + label { background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E");}.todo-list li label { word-break: break-all; padding: 15px 15px 15px 60px; display: block; line-height: 1.2; transition: color 0.4s;}.todo-list li.completed label { color: #d9d9d9; text-decoration: line-through;}.todo-list li .destroy { display: none; position: absolute; top: 0; right: 10px; bottom: 0; width: 40px; height: 40px; margin: auto 0; font-size: 30px; color: #cc9a9a; margin-bottom: 11px; transition: color 0.2s ease-out;}.todo-list li .destroy:hover { color: #af5b5e;}.todo-list li .destroy:after { content: "×";}.todo-list li:hover .destroy { display: block;}.todo-list li .edit { display: none;}.todo-list li.editing:last-child { margin-bottom: -1px;}.footer { color: #777; padding: 10px 15px; height: 20px; text-align: center; border-top: 1px solid #e6e6e6;}.footer:before { content: ""; position: absolute; right: 0; bottom: 0; left: 0; height: 50px; overflow: hidden; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);}.todo-count { float: left; text-align: left;}.todo-count strong { font-weight: 300;}.filters { margin: 0; padding: 0; list-style: none; position: absolute; right: 0; left: 0;}.filters li { display: inline;}.filters li a { color: inherit; margin: 3px; padding: 3px 7px; text-decoration: none; border: 1px solid transparent; border-radius: 3px;}.filters li a:hover { border-color: rgba(175, 47, 47, 0.1);}.filters li a.selected { border-color: rgba(175, 47, 47, 0.2);}.clear-completed,html .clear-completed:active { float: right; position: relative; line-height: 20px; text-decoration: none; cursor: pointer;}.clear-completed:hover { text-decoration: underline;}.info { margin: 50px auto 0; color: #bfbfbf; font-size: 15px; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); text-align: center;}.info p { line-height: 1;}.info a { color: inherit; text-decoration: none; font-weight: 400;}.info a:hover { text-decoration: underline;}@media screen and (-webkit-min-device-pixel-ratio: 0) { .toggle-all, .todo-list li .toggle { background: none; } .todo-list li .toggle { height: 40px; }}@media (max-width: 430px) { .footer { height: 50px; } .filters { bottom: 10px; }}
js
const app = new Vue({ el: "#todoapp", data: { // 总数据 todoList: ["吃饭饭", "睡觉觉", "写代码"], // 输入的内容 inputValue: "", }, // 方法 methods: { // 增加任务 addTodo() { this.todoList.push(this.inputValue); }, // 删除任务 delTodo(index) { this.todoList.splice(index, 1); }, clearTodo() { this.todoList = []; } }});
2、天气预报
可动态查看实时天气情况
html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>天知道</title> <link rel="stylesheet" href="css/reset.css" /> <link rel="stylesheet" href="css/index.css" /></head><body> <div class="wrap" id="app"> <div class="search_form"> <div class="logo"><img src="img/logo.png" alt="logo" /></div> <div class="form_group"> <input type="text" v-model="city" @keyup.enter="searchWeather" class="input_txt" placeholder="请输入查询的天气"/> <button class="input_sub"> 搜 索 </button> </div> <div class="hotkey"> <a href="javascript:;">北京</a> <a href="javascript:;">上海</a> <a href="javascript:;">广州</a> <a href="javascript:;">深圳</a> </div> </div> <ul class="weather_list"> <li v-for="item in weatherList"> <div class="info_type"><span class="iconfont">{{ item.type }}</span></div> <div class="info_temp"> <b>{{ item.low }}</b> ~ <b>{{ item.high }}</b> </div> <div class="info_date"><span>{{ item.date }}</span></div> </li> </ul> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 官网提供的 axios 在线地址 --> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <!-- 自己的js --> <script src="./js/main.js"></script></body></html>
css-1
body{ font-family:'Microsoft YaHei'; }.wrap{ position: fixed; left:0; top:0; width:100%; height:100%; background:#fff;}.search_form{ width:640px; margin:100px auto 0;}.logo img{ display:block; margin:0 auto;}.form_group{ width:640px; height:40px; margin-top:45px;}.input_txt{ width:538px; height:38px; padding:0px; float:left; border:1px solid #41a1cb; outline:none; text-indent:10px;}.input_sub{ width:100px; height:40px; border:0px; float: left; background-color: #41a1cb; color:#fff; font-size:16px; outline:none; cursor: pointer; position: relative;}.input_sub.loading::before{ content:''; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: url('../img/loading.gif');}.hotkey{ margin:3px 0 0 2px;}.hotkey a{ font-size:14px; color:#666; padding-right:15px;}.weather_list{ height:200px; text-align:center; margin-top:50px; font-size:0px;}.weather_list li{ display:inline-block; width:140px; height:200px; padding:0 10px; overflow: hidden; position: relative; background:url('../img/line.png') right center no-repeat; background-size: 1px 130px;}.weather_list li:last-child{ background:none;}.info_date{ width:100%; height:40px; line-height:40px; color:#999; font-size:14px; left:0px; bottom:0px; margin-top: 15px;}.info_date b{ float: left; margin-left:15px;}.info_type span{ color:#fda252; font-size:30px; line-height:80px;}.info_temp{ font-size:14px; color:#fda252;}.info_temp b{ font-size:13px;}.tem .iconfont { font-size: 50px; }
css-2
body,ul,h1,h2,h3,h4,h5,h6{ margin: 0; padding: 0;}h1,h2,h3,h4,h5,h6{ font-size:100%; font-weight:normal;}a{ text-decoration:none;}ul{ list-style:none;}img{ border:0px;}.clearfix:before,.clearfix:after{ content:''; display:table; }.clearfix:after{ clear:both;}.clearfix{ zoom:1;}.fl{ float:left;}.fr{ float:right;}
js
var app = new Vue({ el:"#app", data:{ city:'', weatherList:[] }, methods: { searchWeather:function(){ // console.log('天气查询'); // console.log(this.city); // 调用接口 // 保存this var that = this; axios.get('http://wthrcdn.etouch.cn/weather_mini?city='+this.city) .then(function(response){ // console.log(response); console.log(response.data.data.forecast); that.weatherList = response.data.data.forecast }) .catch(function(err){}) } }, })
3、音乐播放器
html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>悦听player</title> <!-- 样式 --> <link rel="stylesheet" href="./css/index.css"></head><body> <div class="wrap"> <!-- 播放器主体区域 --> <div class="play_wrap" id="player"> <div class="search_bar"> <img src="images/player_title.png" alt="" /> <!-- 搜索歌曲 --> <input type="text" autocomplete="off" v-model="query" @keyup.enter="searchMusic" /> </div> <div class="center_con"> <!-- 搜索歌曲列表 --> <div class='song_wrapper'> <ul class="song_list"> <li v-for="item in musicList"> <a href="javascript:;" @click="playMusic(item.id)"></a> <b>{{ item.name }}</b> <span v-if="item.mvid!=0" @click="playMV(item.mvid)"><i></i></span> </li> </ul> <img src="images/line.png" class="switch_btn" alt=""> </div> <!-- 歌曲信息容器 --> <div class="player_con" :class="{playing:isPlaying}"> <img src="images/player_bar.png" class="play_bar" /> <!-- 黑胶碟片 --> <img src="images/disc.png" class="disc autoRotate" /> <img :src="musicCover" class="cover autoRotate" /> </div> <!-- 评论容器 --> <div class="comment_wrapper"> <h5 class='title'>热门留言</h5> <div class='comment_list'> <dl v-for="item in hotComments"> <dt><img :src="item.user.avatarUrl" alt=""></dt> <dd class="name">{{ item.nickname}}</dd> <dd class="detail"> {{ item.content }} </dd> </dl> </div> <img src="images/line.png" class="right_line"> </div> </div> <div class="audio_con"> <audio ref='audio' @play="play" @pause="pause" :src="musicUrl" controls autoplay loop class="myaudio"></audio> </div> <div class="video_con" v-show="isShow" style="display: none;"> <video :src="mvUrl" controls="controls"></video> <div class="mask" @click="hide"></div> </div> </div> </div> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 官网提供的 axios 在线地址 --> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="./js/main.js"></script></body></html>
css
body,ul,dl,dd { margin: 0px; padding: 0px;}.wrap { position: fixed; left: 0; top: 0; width: 100%; height: 100%; background: url("../images/bg.jpg") no-repeat; background-size: 100% 100%;}.play_wrap { width: 800px; height: 544px; position: fixed; left: 50%; top: 50%; margin-left: -400px; margin-top: -272px; }.search_bar { height: 60px; background-color: #1eacda; border-top-left-radius: 4px; border-top-right-radius: 4px; display: flex; align-items: center; justify-content: space-between; position: relative; z-index: 11;}.search_bar img { margin-left: 23px;}.search_bar input { margin-right: 23px; width: 296px; height: 34px; border-radius: 17px; border: 0px; background: url("../images/zoom.png") 265px center no-repeat rgba(255, 255, 255, 0.45); text-indent: 15px; outline: none;}.center_con { height: 435px; background-color: rgba(255, 255, 255, 0.5); display: flex; position: relative;}.song_wrapper { width: 200px; height: 435px; box-sizing: border-box; padding: 10px; list-style: none; position: absolute; left: 0px; top: 0px; z-index: 1;}.song_stretch { width: 600px;}.song_list { width: 100%; overflow-y: auto; overflow-x: hidden; height: 100%;}.song_list::-webkit-scrollbar { display: none;}.song_list li { font-size: 12px; color: #333; height: 40px; display: flex; flex-wrap: wrap; align-items: center; width: 580px; padding-left: 10px;}.song_list li:nth-child(odd) { background-color: rgba(240, 240, 240, 0.3);}.song_list li a { display: block; width: 17px; height: 17px; background-image: url("../images/play.png"); background-size: 100%; margin-right: 5px; box-sizing: border-box;}.song_list li b { font-weight: normal; width: 122px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}.song_stretch .song_list li b { width: 200px;}.song_stretch .song_list li em { width: 150px;}.song_list li span { width: 23px; height: 17px; margin-right: 50px;}.song_list li span i { display: block; width: 100%; height: 100%; cursor: pointer; background: url("../images/table.png") left -48px no-repeat;}.song_list li em,.song_list li i { font-style: normal; width: 100px;}.player_con { width: 400px; height: 435px; position: absolute; left: 200px; top: 0px;}.player_con2 { width: 400px; height: 435px; position: absolute; left: 200px; top: 0px;}.player_con2 video { position: absolute; left: 20px; top: 30px; width: 355px; height: 265px;}.disc { position: absolute; left: 73px; top: 60px; z-index: 9;}.cover { position: absolute; left: 125px; top: 112px; width: 150px; height: 150px; border-radius: 75px; z-index: 8;}.comment_wrapper { width: 180px; height: 435px; list-style: none; position: absolute; left: 600px; top: 0px; padding: 25px 10px;}.comment_wrapper .title { position: absolute; top: 0; margin-top: 10px;}.comment_wrapper .comment_list { overflow: auto; height: 410px;}.comment_wrapper .comment_list::-webkit-scrollbar { display: none;}.comment_wrapper dl { padding-top: 10px; padding-left: 55px; position: relative; margin-bottom: 20px;}.comment_wrapper dt { position: absolute; left: 4px; top: 10px;}.comment_wrapper dt img { width: 40px; height: 40px; border-radius: 20px;}.comment_wrapper dd { font-size: 12px;}.comment_wrapper .name { font-weight: bold; color: #333; padding-top: 5px;}.comment_wrapper .detail { color: #666; margin-top: 5px; line-height: 18px;}.audio_con { height: 50px; background-color: #f1f3f4; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px;}.myaudio { width: 800px; height: 40px; margin-top: 5px; outline: none; background-color: #f1f3f4;}@keyframes Rotate { from { transform: rotateZ(0); } to { transform: rotateZ(360deg); }}.autoRotate { animation-name: Rotate; animation-iteration-count: infinite; animation-play-state: paused; animation-timing-function: linear; animation-duration: 5s;}.player_con.playing .disc,.player_con.playing .cover { animation-play-state: running;}.play_bar { position: absolute; left: 200px; top: -10px; z-index: 10; transform: rotate(-25deg); transform-origin: 12px 12px; transition: 1s;}.player_con.playing .play_bar { transform: rotate(0);}.search_history { position: absolute; width: 296px; overflow: hidden; background-color: rgba(255, 255, 255, 0.3); list-style: none; right: 23px; top: 50px; box-sizing: border-box; padding: 10px 20px; border-radius: 17px;}.search_history li { line-height: 24px; font-size: 12px; cursor: pointer;}.switch_btn { position: absolute; right: 0; top: 0; cursor: pointer;}.right_line { position: absolute; left: 0; top: 0;}.video_con video { position: fixed; width: 800px; height: 546px; left: 50%; top: 50%; margin-top: -273px; transform: translateX(-50%); z-index: 990;}.video_con .mask { position: fixed; width: 100%; height: 100%; left: 0; top: 0; z-index: 980; background-color: rgba(0, 0, 0, 0.8);}.video_con .shutoff { position: fixed; width: 40px; height: 40px; background: url("../images/shutoff.png") no-repeat; left: 50%; margin-left: 400px; margin-top: -273px; top: 50%; z-index: 995;}
js
var app = new Vue({ el: "#player", data: { // 查询关键字 query: "", // 歌曲数组 musicList: [], // 歌曲地址 musicUrl: "", // 歌曲封面 musicCover: "", // 歌曲评论 hotComments: [], // 动画播放状态 isPlaying: false, // 遮罩层的显示状态 isShow: false, // mv地址 mvUrl: "" }, methods: { // 歌曲搜索 searchMusic: function() { var that = this; axios.get("https://autumnfish.cn/search?keywords=" + this.query).then( function(response) { // console.log(response); that.musicList = response.data.result.songs; console.log(response.data.result.songs); }, function(err) {} ); }, // 歌曲播放 playMusic: function(musicId) { // console.log(musicId); var that = this; // 获取歌曲地址 axios.get("https://autumnfish.cn/song/url?id=" + musicId).then( function(response) { // console.log(response); // console.log(response.data.data[0].url); that.musicUrl = response.data.data[0].url; }, function(err) {} ); // 歌曲详情获取 axios.get("https://autumnfish.cn/song/detail?ids=" + musicId).then( function(response) { // console.log(response); // console.log(response.data.songs[0].al.picUrl); that.musicCover = response.data.songs[0].al.picUrl; }, function(err) {} ); // 歌曲评论获取 axios.get("https://autumnfish.cn/comment/hot?type=0&id=" + musicId).then( function(response) { // console.log(response); // console.log(response.data.hotComments); that.hotComments = response.data.hotComments; }, function(err) {} ); }, // 歌曲播放 play: function() { // console.log("play"); this.isPlaying = true; }, // 歌曲暂停 pause: function() { // console.log("pause"); this.isPlaying = false; }, // 播放mv playMV: function(mvid) { var that = this; axios.get("https://autumnfish.cn/mv/url?id=" + mvid).then( function(response) { // console.log(response); console.log(response.data.data.url); that.isShow = true; that.mvUrl = response.data.data.url; }, function(err) {} ); }, // 隐藏 hide: function() { this.isShow = false; } }});