简单的Vue脚手架案例

实现效果

image

实现步骤

  1. 通过 vue-cli 脚手架工具, 初始化项目, 命名为test_project;
    • 在 components 目录下创建 Header.vue 组件、 List.vue 组件、 Recommend.vue 组件;
    • 在 src 目录先新建 page 文件夹, 在 page 文件夹下新建 Home.vue 组件;
    • img 文件夹(提供)、iconfont 文件夹(提供) 放在 static 目录下;
    • 在public文件夹放json文件
  2. Home.vue组件引入List.vue 组件、 Recommend.vue 组件,App.vue 组件中引入Header.vue 组件;

具体操作

  1. 安装vue-cli,我是用yarn安装的,需要提前安装yarn,也可以用npm安装,创建项目
yarn global add @vue/cli
//创建项目
vue create my-project
  1. vsCode添加此项目,在有package.json的情况下,可以使用npm i或者cnpm i安装package.json中需要的包(cnpm是淘宝镜像,没有的话需要安装)
cnpm i
//或者 yarn serve
npm run serve
//安装axios包
yarn add axios
  1. 将文件放入指定文件夹,如下为相关文件
//home.json
{
    "status": "success",
    "data": {
        "listInfo": [
            {
                "id": 1,
                "imgUrl": "//upload-images.jianshu.io/upload_images/15035171-da8ef5127e7628b0.png@c_1,w_640,h_402,x_0,y_0?imageMogr2/auto-orient/strip|imageView2/1/w/360/h/240",
                "title": "21句正能量的句子,让你在自律路上多点思考",
                "desc": "1、如果非要说成功有捷径,我想应该是每天做一点点有益小事,持续执行,保持自律状态。 2、当我们每天带着开天辟地的隆重感,去把平凡小事完成好。"
            },
            {
                "id": 2,
                "imgUrl": "//upload-images.jianshu.io/upload_images/12830911-487d64e0b633a495.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/360/h/240",
                "title": "坚持跑步十四年,我一斤没瘦",
                "desc": "不知不觉,从2005年的春天开始跑步,到今年整整十四年了。 我从一开始一个人孤独的跑步,最多跑个五公里,到拖着仇先生一起跑,慢慢加长到十公里,直..."
            },
            {
                "id": 3,
                "imgUrl": "//upload-images.jianshu.io/upload_images/16039452-d910371f88cb26d3.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/360/h/240",
                "title": "无聊翻到的小程序居然给我带来了现金收益?",
                "desc": "最近在家无聊,在翻微信的过程中,无意间发现了个小程序,是类似于知乎的,但又和知乎不一样,最主要的是,这个小程序居然可以给我转到一些外快?"
            },
            {
                "id": 4,
                "imgUrl": "//upload-images.jianshu.io/upload_images/15177263-cb5d496aa0d1deeb.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/360/h/240",
                "title": "成为数据分析师,抢占互联网红利!",
                "desc": "师从硅谷名企,洞察数据秘密,成为互联网高薪人才!"
            }
        ],
        "recommendlist": [
            {
                "id": 1,
                "imgUrl": "http://cdn2.jianshu.io/assets/web/banner-s-club-aa8bdf19f8cf729a759da42e4a96f366.png"
            },
            {
                "id": 2,
                "imgUrl": "http://cdn2.jianshu.io/assets/web/banner-s-3-ddcc844ebdd8edca2d93b7ea5a8de79e.png"
            },
            {
                "id": 3,
                "imgUrl": "http://cdn2.jianshu.io/assets/web/banner-s-7-1a0222c91694a1f38e610be4bf9669be.png"
            }
        ]
    }
}
  1. 如下为文件夹放置示意图


    image
  2. Header.vue
    此处需要注意的是iconfont的引入及使用,这边文件里是用的unicode
<template>
  <header>
    <div class="logo">
      <img src="../static/img/logo.png" alt />
    </div>
    <div class="middle mar">
      <span>首页</span>
      <span>下载APP</span>
      <span class="input">
        <input type="text" placeholder="搜索" />
        <span style="padding:0 10px;" class="iconfont">&#xe60c;</span>
      </span>
    </div>
    <div class="mar" style="color:#ccc">
        <span>登录</span>
        <span class="iconfont">&#xe636;</span>
    </div>
    <div class="mar">
        <span style="border:1px solid orangered;padding:5px 20px;border-radius:20px;color:orangered">注册</span>
        <span class="iconfont" style="color:#fff;border:1px solid orangered;padding:11px 20px;border-radius:20px;background-color:orangered">&#xe61b;写文章</span>
    </div>
  </header>
</template>

<script>
export default {};
</script>

<style scoped>
//引入iconfont
@import '//at.alicdn.com/t/font_1589357_wgxgrvh5n9c.css';
@import '../static/iconfont/iconfont.css';
header {
    width: 100vw;
  display: flex;
  justify-content: space-around;
  align-items: center;
  height: 80px;
  border-bottom: 1px solid #eee;
}
.logo img {
  height: 80px;
}
.middle {
  width: 500px;
  display: flex;
}
.mar >* {
  margin: 0 10px;
}
input {
  height: 40px;
  border: none;
  text-indent: 1em;
  flex: 1;
  background-color: transparent;
}
.input {
  width: 230px;
  display: flex;
  justify-content: space-between;
  background-color: #eee;
  align-items: center;
  border-radius: 20px;
}
</style>
  1. List.vue
<template>
  <div>
    <div
      style="padding:20px 0;border-bottom:1px solid #eee;display:flex;justify-content:space-between;"
      v-for="(item, index) in listInfo"
      :key="index"
    >
      <div style="padding:0 5px;">
        <div style="font-weight:600;line-height:34px">{{item.title}}</div>
        <div style="color:#ccc;font-size:14px;line-height:24px;">{{item.desc}}</div>
      </div>
      <div><img width="120px" style="border-radius:8px" :src="item.imgUrl" alt=""></div>
    </div>
  </div>
</template>

<script>
export default {
    props:{
        listInfo:{
            type:Array,
            required:true
        }
    }
  }
};
</script>

<style scoped>
</style>
  1. Recommend.vue
<template>
  <div>
    <div style="margin:10px 0;opacity:0.8;" v-for="(item, index) in recommendlist" :key="index">
      <img width="320px" :src="item.imgUrl" alt />
    </div>
  </div>
</template>

<script>
export default {
  props: {
    recommendlist: {
      type: Array,
      required: true
    }
  }
};
</script>

<style scoped>
</style>
  1. Home.vue引入List.vue和Recommend.vue组件,此处需要用axios引入json。
    这里需要注意的是,静态的json文件需放在public文件夹,用axios.get('data.json')即可,不加public路径,或者放在其他文件夹,用require引入也可
<template>
  <main>
    <div class="left">
      <img style="border-radius:8px;" width="650px" src="../static/img/home.png" alt />
      <List :listInfo='listInfo'></List>
    </div>
    <div>
      <Recommend :recommendlist='recommendlist'></Recommend>
    </div>
  </main>
</template>

<script>
import List from "../components/List.vue";
import Recommend from "../components/Recommend.vue";
import axios from 'axios'

export default {
  components: {
    List,
    Recommend
  },
  mounted() {
    axios.get('home.json').then(res=>{
    this.listInfo=res.data.data.listInfo
    this.recommendlist=res.data.data.recommendlist
})
    
  },
  data() {
    return {
      listInfo: [],
      recommendlist:[]
    }
  },
};
</script>

<style scoped>
main{
    width: 1050px;
    margin: 10px auto;
    display: flex;
    justify-content: space-between;
}
.left{
    width: 650px;
}
</style>
  1. App.vue
<template>

    <div id="app">
        <Header></Header>
        <Home></Home>
        
    </div>

</template>

<script>
    import Header from './components/Header.vue'
import Home from './page/Home.vue'
    export default {
        name:'APP',
        components:{
           Header ,
           Home
        }
}

</script>

<style scoped>
*{
    margin: 0;
    padding: 0;
}
</style>

  1. index.js
    因为Home.vue是可以切换的,所以我将它设置成了路由,保存在src/router中,由于大部分情况下路由很多,所以单独写在index.js文件中,再将他暴露出去
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../page/Home'
Vue.use(VueRouter)
Vue.use(Home)
let  router=new VueRouter({
    routes: [{
        path: '/',
        component: Home
    }]
})
export default router
  1. main.js
import Vue from 'vue'
import App from './App'
import router from './router/index.js'
Vue.use(App)
new Vue({
    el:'#app',
    router,
    render:h=>h(App)
})

效果图

image

补充路由

懒加载

  • 当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
  • Vue中实现懒加载有如下两种方法
    • 被 Webpack 自动代码分割的异步组件
    const Foo = () => import('./Foo.vue')
    const router = new VueRouter({
      routes: [
        { path: '/foo', component: Foo }
      ]
    })
    
    • 把组件按组分块:把某个路由下的所有组件都打包在同个异步块 (chunk) 中
    const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
    const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
    const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
    
  • 为了防止所有组件打包到App.js中导致文件过大,首屏加载缓慢,使用注释分组打包
let  router=new VueRouter({
    routes: [{
        path: '/',
        component:()=>import (/* webpackChunkName: "group-foo" */ '../page/Home')
    },{
        path: '/login',
        component: ()=>import (/* webpackChunkName: "group-foo" */ '../page/Login')
    }]
})

路由元信息

  • 定义路由的时候可以配置 meta 字段
let  router=new VueRouter({
    routes: [{
        path: '/',
        meta:{title:'首页'},
        component:()=>import (/* webpackChunkName: "group-foo" */ '../page/Home')
    },{
        path: '/login',
        meta:{title:'登录'},
        component: ()=>import (/* webpackChunkName: "group-foo" */ '../page/Login')
    }]
})
  • 在全局导航守卫中可以获取元字段

导航守卫

  • 通过跳转或取消的方式守卫导航
  • 全局前置守卫 :路由跳转之前被拦截
router.beforeEach((to, from, next) => {
  // if(×××) next()
})
  • 全局后置钩子:路由跳转之后被拦截
router.afterEach((to, from) => {
  // ...
})
  • 通过导航守卫制作头部的process进度条,通过判断有无原信息的title,控制切换网页的title
//App.vue
<template>
  <div id="app">
    <div id="progress"></div>
    <Header></Header>
    <router-view></router-view>
  </div>
</template>

<script>
import Header from "./components/Header.vue";
export default {
  components: {
    Header
  }
};
</script>

<style scoped>
* {
  margin: 0;
  padding: 0;
}
#progress {
  height: 5px;
  background: red;
  position: fixed;
  top: 0;
  left: 0;
  animation: grow 1s infinite;
  display: none;
}
@keyframes grow {
  from {
    width: 0px;
  }
  to {
    width: 100%;
  }
}
</style>
//router/index.js
router.beforeEach((to, from, next) => {
    let pro =document.getElementById('progress')
    if(pro) pro.style.display='block'
    //to代表去往的页面,若有元信息的title,则网页的title换成to.meta.title
    if(to.meta&&to.meta.title) document.title=to.meta.title
    console.log(to)
    setTimeout(() => {
        next()
    }, 1000);
})
router.afterEach(() => {
    // to and from are both route objects.
    let pro =document.getElementById('progress')
    if(pro) pro.style.display='none'
})

小技巧

Vue中添加方法

  • 混入,其他文件引入时import+路径即可
import Vue from 'vue'
import axios from 'axios'
Vue.mixin({
    methods: {
        $get(url,data){
            return axios(url,{params:data})
        }
    },
})
  • 将方法加在Vue的原型对象上
import axios from "axios";
Vue.prototype.$axios=axios

其他

  • @代表src文件夹,可以用它避免相对引用。
  • 当要读取的时index命名的文件时,可以不写index,如下:
import '@/mixin'
  • public放置静态资源
  • Vue的源码中有一段关于Vue.use的定义,从源码中我们可以发现vue首先判断这个插件是否被注册过,如果已经注册过就直接 return 这个插件。并且接收的plugin参数的限制是Function | Object两种类型。
    • 如果我们传入一个对象,对象中包含install方法,那么我们就调用这个plugin的install方法并将整理好的数组当成参数传入install方法中。
    • 如果我们传入的plugin就是一个函数,那么我们就直接调用这个函数并将整理好的数组当成参数传入。
    • Vue.js 官方提供的一些插件 (例如 vue-router) 在检测到 Vue 是可访问的全局变量时会自动调用 Vue.use()。然而在像 CommonJS 这样的模块环境中,你应该始终显式地调用 Vue.use()
function initUse (Vue) {
    Vue.use = function (plugin) {
      var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
      if (installedPlugins.indexOf(plugin) > -1) {
        return this
      }

      // additional parameters
      var args = toArray(arguments, 1);
      args.unshift(this);
      if (typeof plugin.install === 'function') {
        plugin.install.apply(plugin, args);
      } else if (typeof plugin === 'function') {
        plugin.apply(null, args);
      }
      installedPlugins.push(plugin);
      return this
    };
  }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容

  • 前言 vue-router是什么:是vue.js官方的路由管理器和vue.js的核心深度的集成,让开发者更加简单的...
    GUAN_one阅读 3,709评论 0 2
  • vue-cli搭建项目 确保安装了node与npm 再目标文件夹下打开终端 执行cnpm i vue-cli -g...
    Akiko_秋子阅读 3,227评论 1 22
  • 1、组件 1.1 创建组件 在components里面放置我们自己写的组件,例如在components文件夹里面创...
    Juntech阅读 361评论 0 0
  • Vue八个生命周期 beforeCreate【创建前】created【创建后】 beforeMount【载入前】 ...
    艾萨克菊花阅读 1,314评论 0 12
  • ## 框架和库的区别?> 框架(framework):一套完整的软件设计架构和**解决方案**。> > 库(lib...
    Rui_bdad阅读 2,906评论 1 4