Vue2.0组件实现动态搜索引擎(一)

来源:互联网 发布:竞翔通软件 编辑:程序博客网 时间:2024/06/06 02:31

从github上看到一个不错的开源项目:https://github.com/lavyun/vue-demo-search,

自己琢磨着不看代码做了一遍学习了不少,现将碰到的坑与填坑过程分享出来。


首先看一下demo的界面:



简单来说就是一个复刻各大搜索引擎的功能,用户输入关键字能出现搜索建议并上下键控制输入框内容。




同时点击上方logo能够切换不同引擎,点击搜一下能跳转到对应网站搜索结果。

首先分析一下页面,基本由2个部分组成:上方的LOGO部分和下方的输入框与搜索建议弹框。

由于篇幅关系,这次先分析logo部分的实现代码。

基于这次练习是针对Vue组件,所以我们可以将其拆分为logo组件和搜索组件,并将其设为父子组件方便初学,将来熟练以后可以考虑变为更常见的兄弟组件并使用event bus或者vuex来实现组件通信。


开发环境: Vue2.0、Node.js、npm、webpack、vue-cli、vue-resources、webstorm


为了以后项目工程化的目标,所以我们使用node+npm+webpack来构建项目。

准备工作包括安装node,npm,然后依次安装webpack,vue-cli

具体教程网上很多,就不在此赘述了。


1、选择一个文件夹放工程,cmd进入该目录

cd 目录路径(这里有个小坑,cd命令只对路径当前盘符有效果,例如在c盘输入d:../...是没有效果的还要再输入d:回车或者先进入d盘再cd 路径)


2、创建项目

vue init webpack-simple 工程名字(不能使用中文)

会有一些初始化的设置,如下输入:

Target directory exists. Continue? (Y/n)直接回车默认(然后会下载 vue2.0模板,慢的话连vpn)

Project name (vue-test)直接回车默认

Project description (A Vue.js project) 直接回车默认

 use sass?(Y/n)是否使用sass,选n回车

Author 写你自己的名字


3、安装项目依赖

npm install(npm服务器在国外可能会很慢,实在不行挂vpn)


4、启动项目

npm run dev

正常的话默认浏览器就会打开页面了,如图:




这就是vue默认模板了,我们需要修改掉,开始建我们自己的项目。

首先修改src文件夹下的index.html:

<!DOCTYPE html><html>  <head>      <meta charset="utf-8">      <title>Vue搜索</title>  </head>  <body>      <div id="app1"></div>  </body></html>
再到src下的main.js:        

import Vue from 'vue';import App from './App1.vue';var vueResource = require('vue-resource');Vue.use(vueResource);new Vue({  el: '#app',  render: h => h(App)})

注意这里import App from ‘./App1.vue’对应项目vue文件,要使用vue resources也要在这里声明。

然后在src下创建App1.vue:

<template>  <div id="app">      <panel></panel>  </div></template><script>  import panel from './components/panel-new.vue'  export default {    components: {        panel    }  }</script>

需要说明的是,vue文件一般来说由<template></template>、<script></script>、<style></style>组成,对应放html、js和css代码。

这里出现第一个坑,就是template内只放一个div!

就算有并列的兄弟组件也还是要写成:

<template>
<div>
<logo></logo>
<content></content>
<ending></ending>
</div>
</template>

而不能是:

<template>
<div>
<logo></logo>
</div>
<div>
<content></content>
</div>
<div>
<ending></ending>
</div>
</template>

否则会报错。

引入组件后用export default导出到template中,此处有第二个bug:

千万不要在单文件组件(.vue)中再次new Vue()!

因为已经在main.js中创建过实例了,重复的实例将会出现意外,详情见我在seqement fault中的提问:

https://segmentfault.com/q/1010000009870708?_ea=2079044


接下来就是最关键的组件部分,在src下创建components文件夹,再新建logo-new.vue:

<template>  <div class="logo">    <img class="logoNow" :src="imgs[imgState].imgSrc" @click="toggle">    <div class="triangle" @click="toggle">      <span></span>    </div>    <div class="logoMain">      <transition name="fade">        <ul v-show="toggleState" class="listLogo" @mouseleave="leaveList">          <li v-for="(item, index) in imgs" :class=" index == imgSelected ? 'colorBack' : ''" @click="changeImg(index)"              @mouseover="changeBackColor(index)">            <img :src="item.imgSrc">          </li>        </ul>      </transition>    </div>  </div></template><script>  export default {    data () {      return {        //下拉图片背景初始值        imgSelected: -1,        //判断下拉条件        toggleState: false,        //界面显示哪张图片        imgState: 0,        //图片一类的静态文件,应该放在这个static文件夹下,        // 这个文件夹下的文件(夹)会按照原本的结构放在网站根目录下        imgs: [{          imgSrc: ('.././static/360_logo.png')        }, {          imgSrc: ('.././static/baidu_logo.png')        }, {          imgSrc: ('.././static/sougou_logo.png')        }]      }    },    methods: {      toggle: function () {        this.toggleState = !this.toggleState,        //清空上次背景色,        this.imgSelected = -1      },      changeImg: function (index) {        this.toggleState = !this.toggleState,        this.imgState = index,        this.$emit('logoNow', [index])      },      changeBackColor: function (index) {        this.imgSelected = index      },      leaveList: function () {          this.toggleState = false,          this.imgSelected = -1      }    }  }</script><style>  ul {    padding: 0;    margin: 0;  }  .logoMain {    position: relative;  }  .listLogo {    z-index: 9999;    position: absolute;    top: 50%;    left: 60%;    width: 200px;    margin-left:-100px;    /*border: 1px solid #fefefe;*/  }  li {    background-color: #fefefe;    list-style-type: none;    width: 200px;    margin-left: -50px;  }  /*li:hover {    background-color: #ccc;  }*/  li img {    cursor: pointer;    width: 200px;    height: 58.33px;  }  .logoNow, .triangle{    cursor: pointer;   }  .triangle {    display: inline-block;    position: relative;    left: -80px;    top: -70px;  }  .triangle span {    position: absolute;    display: inline-block;    width: 0;    height: 0;    border-width: 8px;    border-color: #000 transparent transparent transparent;    border-style: solid dashed dashed dashed;  }  .fade-enter-active, .fade-leave-active {    transition: all .5s;  }  /* .fade-leave-active 在 <2.1.8 中 */  .fade-enter, .fade-leave-to  {    opacity: 0;    -webkit-transform: translateY(20px);    -moz-transform: translateY(20px);    -ms-transform: translateY(20px);    -o-transform: translateY(20px);    transform: translateY(20px);  }  .colorBack {    background-color: #ccc;    cursor: pointer;  }</style>

说下整体思想:

点击logo图片可以弹出下拉框选择3个不同的搜索引擎,再点击选项替换logo的img src。

因为vue是以mvvc模式,任何操作都要与模型(数据)挂钩,不能再用dom的思想;所以我们在data里给img设置一个数组,再由一个变量给定具体数值作为下标判断其显示状态。至于下拉菜单部分可以设置为一个ul列表,再用v-show指令控制其显示与否,同样用一个布尔值变量toggleState来判断,默认为false,一旦点击logo图片就会使其变为true,进而出现下拉菜单。


这里的重点在于如何将点击的li与切换到对应logo图片挂上钩?

原生js的思想是用一个for循环,获取到当前‘i’的值再去处理。Vue中也有类似的功能,在v-for循环中有一个参数‘index’可以表示当前索引,将其赋值给下标变量即可完成动态logo图片切换。


另一个难点在于如何在列表li添加hover变色效果?

最简单的方法自然是css中添加li:hover{background:#xxx},

但这显然不是我们想要的方法,用了vue自然要利用其双边绑定的特性。

给li设置一个监听mouseover的方法,将当前index赋值给一个变量,再用一个三元表达式绑定class即只要变量值与当前index相等,则添加一个带背景色的class,否则class为空。


最后一个问题则是给下拉菜单添加过渡效果

vue封装了css3的transition效果,只要在元素上添加name属性,然后在style中用name-xxx指定参数即可,具体详见官网https://cn.vuejs.org/v2/api/#transition

还有一个坑就是给多个元素/组件设置过渡效果就要使用transition-group标签,像我们这个ul列表就是要的。

另外一个问题就是使用的时候一定要紧贴元素外层,不能中间隔了一个div,这样是没有效果的。


到此logo部分的组件大致完成,panel组件请留意我下一篇博文。

博主也是初学vue,并不精深,有出入的地方烦请各位看官指出!

原创粉丝点击