Vue快速入门(附实战小项目:记事本、天气预报、音乐播放器)

简介: Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。​ Vue通过MVVM模式,能够实现视图与模型的双向绑定。​ 简单来说,就是数据变化的时候, 页面会自动刷新, 页面变化的时候,数据也会自动变化.

一、前言



Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。


Vue通过MVVM模式,能够实现视图与模型的双向绑定。

简单来说,就是数据变化的时候, 页面会自动刷新, 页面变化的时候,数据也会自动变化.


二、Vue.js安装



NPM方法(推荐使用)


1.安装node.js

从node.js官网下载并安装node,安装过程很简单,一直点下一步就ok了,输入

node -v命令,查看node的版本


2.npm -v


3.安装cnpm

在命令行中输入 npm install -g cnpm --registry=http://registry.npm.taobao.org


4.安装vue-cli2 脚手架构建工具(必须在全局中进行安装)

在命令行中运行命令 npm install -g vue-cli ,然后等待安装完成。

是否安装成功:vue -V


三、初始化Vue项目



命令:vue init webpack firstApp

输入命令后,会询问我们几个简单的选项,我们根据自己的需要进行填写就可以了。


  1. Project name :项目名称 ,如果不需要更改直接回车就可以了。注意:这里不能使用大写,所以我把名称改成了vueclitest
  2. Project description:项目描述,默认为A Vue.js project,直接回车,不用编写。
  3. Author:作者,如果你有配置git的作者,他会读取。
  4. Install vue-router? 是否安装vue的路由插件,我们这里需要安装,所以选择Y
  5. Use ESLint to lint your code? 是否用ESLint来限制你的代码错误和风格。我们这里不需要输入n(建议),如果你是大型团队开发,最好是进行配置。
  6. setup unit tests with Karma + Mocha? 是否需要安装单元测试工具Karma+Mocha,我们这里不需要,所以输入n。
  7. Setup e2e tests with Nightwatch?是否安装e2e来进行用户行为模拟测试,我们这里不需要,所以输入n


四、项目目录解析



  1. build:最终发布的代码的存放位置。


  1. config:配置路径、端口号等一些信息,我们刚开始学习的时候选择默认配置。


  1. node_modules:npm 加载的项目所需要的各种依赖模块。


  1. src:这里是我们开发的主要目录(源码),基本上要做的事情都在这个目录里面,里面包含了几个目录及文件:


1.assets:放置一些图片(会根据图片大小分类进行base64命名还是其他方式命名),如logo等


2. components:目录里放的是一个个的组件文件

3.router/index.js:配置路由的地方


5.App.vue:项目入口组件(跟组件),我们也可以将组件写这里,而不使用components目录。主要作用就是将我们自己定义的组件通过它与页面建立联系进行渲染,这里面的必不可少。


6.main.js :项目的核心文件(整个项目的入口js)引入依赖包、默认页面样式等(项目运行后会在index.html中形成一个app.js文件)。


7.static:静态资源目录(会原分不动的对文件进行处理),如图片、字体等。


8.test:初始测试目录,可删除


9..XXXX文件:配置文件。


10.index.html:html单页面的入口页面,可以添加一些meta信息或者同统计代码啥的或页面的重置样式等。


11.package.json:项目配置信息文件/所依赖的开发包的版本信息及所依赖的插件信息。(大概版本)


12.package-lock.json:项目配置信息文件/所依赖的开发包的版本信息及所依赖的插件信息。(具体版本)


13.README.md:项目的说明文件。


14.webpack.config.js:webpack的配置文件,例:把.vue的文件打包成浏览器能读懂的文件。


15..babelrc:是检测es6语法的配置文件,例:适配哪些浏览器的限制


16..gitignore:上传到服务器忽略哪些文件的配置(比如模拟本地数据mock不让他在get提交/打包上线的时候忽略不使用可在这里配置)


17..postcssrc.js:前缀的配置 (css转化的配置)


18..editorconfig:对代码进行规范,例:root是否进行检测,代码尾部是否换行,缩行前面几个空格…(建议定义这个规范)


19..eslintrc.js:配置eslint语法规则(在这里面的rules属性中配置让哪个语法规则失效)


20..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、记事本


可动态增加、删除、显示总数、一键清除、隐藏

1.png


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; /* Mobile Safari */
  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;
  /* auto, since non-WebKit browsers doesn't support input styling */
  height: auto;
  position: absolute;
  top: 0;
  bottom: 0;
  margin: auto 0;
  border: none; /* Mobile Safari */
  -webkit-appearance: none;
  appearance: none;
}
.todo-list li .toggle {
  opacity: 0;
}
.todo-list li .toggle + label {
  /*
    Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
    IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
  */
  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;
}
/*
  Hack to remove background from Mobile Safari.
  Can't use it globally since it destroys checkboxes in Firefox
*/
@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、天气预报


可动态查看实时天气情况

2.png


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: radial-gradient(#f3fbfe, #e4f5fd, #8fd5f4); */
    /* background:#8fd5f4; */
    /* background: linear-gradient(#6bc6ee, #fff); */
    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;
}
/* .weather_list .col02{
    background-color: rgba(65, 165, 158, 0.8);
}
.weather_list .col03{
    background-color: rgba(94, 194, 237, 0.8);
}
.weather_list .col04{
    background-color: rgba(69, 137, 176, 0.8);
}
.weather_list .col05{
    background-color: rgba(118, 113, 223, 0.8);
} */
.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;
}
/* 清除浮动,解决margin-top塌陷 */
.clearfix:before,.clearfix:after{
    content:'';
    display:table;    
}
.clearfix:after{
    clear:both;
}
.clearfix{
    zoom:1;
}
.fl{
    float:left;
}
.fr{
    float:right;
}


js


/*
  请求地址:http://wthrcdn.etouch.cn/weather_mini
  请求方法:get
  请求参数:city(城市名)
  响应内容:天气信息
  1. 点击回车
  2. 查询数据
  3. 渲染数据
  */
 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、音乐播放器

3.png


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;
  /* background-color: #f9f9f9; */
}
.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


/*
  1:歌曲搜索接口
    请求地址:https://autumnfish.cn/search
    请求方法:get
    请求参数:keywords(查询关键字)
    响应内容:歌曲搜索结果
  2:歌曲url获取接口
    请求地址:https://autumnfish.cn/song/url
    请求方法:get
    请求参数:id(歌曲id)
    响应内容:歌曲url地址
  3.歌曲详情获取
    请求地址:https://autumnfish.cn/song/detail
    请求方法:get
    请求参数:ids(歌曲id)
    响应内容:歌曲详情(包括封面信息)
  4.热门评论获取
    请求地址:https://autumnfish.cn/comment/hot?type=0
    请求方法:get
    请求参数:id(歌曲id,地址中的type固定为0)
    响应内容:歌曲的热门评论
  5.mv地址获取
    请求地址:https://autumnfish.cn/mv/url
    请求方法:get
    请求参数:id(mvid,为0表示没有mv)
    响应内容:mv的地址
*/
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;
    }
  }
});


相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
18天前
|
JavaScript 数据可视化
vue-cli学习一:vue脚手架的 vue-cli2和vue-cli3版本 创建vue项目,vue的初始化详解
这篇文章介绍了如何使用vue-cli 2和3版本来创建Vue项目,并详细说明了两者之间的主要区别。
67 5
vue-cli学习一:vue脚手架的 vue-cli2和vue-cli3版本 创建vue项目,vue的初始化详解
|
12天前
|
JavaScript 容器
乾坤qiankun框架搭建 主应用为vue3的项目。
乾坤qiankun框架搭建 主应用为vue3的项目。
91 2
|
22天前
|
JavaScript
Vue CLi脚手架创建第一个VUE项目
Vue CLi脚手架创建第一个VUE项目
32 3
|
2天前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
18天前
|
JavaScript 数据可视化
vue-cli学习二:vue-cli3版本 创建vue项目后,Runtime-Compiler和Runtime-only两个模式详解;vue项目管理器;配置文件的配置在哪,以及如何配置
这篇文章详细介绍了Vue CLI 3版本创建项目时的Runtime-Compiler和Runtime-only两种模式的区别、Vue程序的运行过程、render函数的使用、eslint的关闭方法,以及Vue CLI 2和3版本配置文件的不同和脚手架3版本创建项目的配置文件配置方法。
27 3
vue-cli学习二:vue-cli3版本 创建vue项目后,Runtime-Compiler和Runtime-only两个模式详解;vue项目管理器;配置文件的配置在哪,以及如何配置
|
5天前
|
JavaScript
如何在 Vue 项目中选择合适的模块格式
【10月更文挑战第20天】选择合适的模块格式需要综合考虑多个因素,没有一种绝对正确的选择。需要根据项目的具体情况进行权衡和分析。在实际选择过程中,要保持灵活性,根据项目的发展和变化适时调整模块格式。
15 7
|
1天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
5天前
|
JavaScript 前端开发 编译器
在 Vue 项目中使用 ES 模块格式的优点
【10月更文挑战第20天】在 Vue 项目中使用 ES 模块格式具有众多优点,这些优点共同作用,使得项目能够更高效、更可靠地开发和运行。当然,在实际应用中,还需要根据项目的具体情况和需求进行合理的选择和配置。
16 6
|
1天前
|
JavaScript 测试技术 UED
解决 Vue 项目中 Tree shaking 无法去除某些模块
【10月更文挑战第23天】解决 Vue 项目中 Tree shaking 无法去除某些模块的问题需要综合考虑多种因素,通过仔细分析、排查和优化,逐步提高 Tree shaking 的效果,为项目带来更好的性能和用户体验。同时,持续关注和学习相关技术的发展,不断探索新的解决方案,以适应不断变化的项目需求。
|
10天前
|
缓存 JavaScript 前端开发
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
45 3

相关实验场景

更多