2020-08-30 前端路上的新长征(Vuetify入门)

image.png

前端路上的新长征

  • 前言

从历史的角度来说,尽管几十年过去了,前端(即运行在浏览器里的程序)的基本结构还是以HTML(CSS)+Javascript为主,变化不大,曾经有一些脚本语言比如微软的VBS试图挑战过这个架构,均以失败告终(甚至连昙花一现都谈不上)。后端(服务器)方面则是百花齐放,方案众多,像是Java、PHP、GO、Python以及Javascript本身的一个分支Node.JS等等。

由于WEB技术的发展速度非常快,因此HTML与Javascript也在高速迭代,于是产生了 “框架” 这个概念,所谓框架就是一堆前人已经编写好的成体系的代码,其主要目的是不重复造轮子,加速开发效率。

前端框架大致上分为两个大类,侧重于HTML的CSS框架与侧重于Javascript的JS框架。

主流的Javascript框架:React、Angular、Vue
主流的CSS框架:Bootstrap、Element、Vuetifylk

像React、Vue这一类框架关注点是Javascript本身,更多的是属于处理逻辑层面的事务。而Bootstrap和Vuetify这一类CSS框架则着重关注HTML界面,页面布局,排版等视觉方面的事情。

在没有框架之前,所有前端开发者都采用原生HTML和原生Javascript,工作量极大。当然,你如果问:我们一定需要框架吗?答案却是否定的,国外就确实有一股势力是反对采用框架编程的,自诩为 "JS反框架联盟",为了不引战,此话题咱们不做深入探讨,见仁见智吧。

  • PS: 本文以Vue + Vuetify为基础。

Vuetify 篇:

  • Vuetify 号称是 Vue.js 的头号组件库,笔者使用过Bootstrap,因为年代久远已经忘得差不多了,具体特点也不做评价。外卖双雄之一的饿了吗也搞了CSS库,名字叫Element,感觉上手很容易,但限制比较多,UI的炫酷程度也比较一般。Vuetify 要比前两者诞生的时间晚,但是它的可定制性很强,速度快,而且预设UI非常新潮,漂亮,值得尝试!

由于Vuetify是专门提供给Vue使用的(从其名字也大概猜到了),所以在使用Vuetify之前我们还必须先安装Vue。

  • 装Vue之前还得先装Node.JS

    Node.JS本质上用于服务器后端,它将原本只能用在前端的Javascript变得可以运行在服务器后端,它对标的是Java、PHP这一类东西。但此处安装Node.js并不是要用到它的后端功能,主要是需要他的一个附属工具:“npm” 包管理工具。

  • 回到Windows cmd命令行界面,开始准备安装vue-cli:

    • vue-cli是vue框架的“脚手架”,它的作用是快速为你搭建一个空的框架,然后立刻可以工作,省去了一大堆调试安装的事情。当然如果你不采用vue脚手架,用传统的方式引用一个vue.js也是可以的。这样做的优点是更加简洁高效,但是由于缺少npm包管理这个工具,调试和链接各种组件的难度将陡增,在此我还是建议从脚手架开始吧,等以后有闲了可以再去深入。

    • 输入以下命令:

      npm install -g @vue/cli
      
    • vue-cli安装好之后,vue命令就能使用了,用它创建一个项目,名称随意(此处为mini):

      vue create mini //创建mini项目
      cd mini
      vue add vuetify //添加vuetify组件
      
      
      • 项目将会自动创建完成,并且自动生成以下目录结构:
image-20200830075007635.png
  • 问题是,如何运行一个vue程序? 很简单,在cmd命令行输入以下命令:

    npm run serve
    

一个最基础的APP界面如下:

image-20200829024214407.png

如图所示,这是一个最简单的APP框架,包括

  • 两个状态栏:v-app-bar(顶部)、v-footer(底部)
  • 侧边功能导航栏:v-navigation-drawer
  • 主视图区:v-content

最基础的主页代码,UI由<template>部分组成,JS代码包裹在<script>里:

<template>
  <v-app id="inspire">
    <v-navigation-drawer
            v-model="drawer"
            app
            clipped>
<!--  vmodel: 显示开关,设为null的时候屏蔽导航栏 
         app : 是app = 'true'的简化语法,表示这个导航栏属于app的一部分
         clipped: 等于clipped = 'true',表示导航栏采用压缩布局
-->
      <v-list dense>
        <v-list-item link>
          <v-list-item-action>
            <v-icon>mdi-home</v-icon>
          </v-list-item-action>

          <v-list-item-content>
            <v-list-item-title>选项1</v-list-item-title>
          </v-list-item-content>
        </v-list-item>

        <v-list-item link>
          <v-list-item-action>
            <v-icon>mdi-email</v-icon>
          </v-list-item-action>

          <v-list-item-content>
            <v-list-item-title>选项2</v-list-item-title>
          </v-list-item-content>
        </v-list-item>
      </v-list>
    </v-navigation-drawer>

    <v-app-bar
            app
            clipped-left
            color="cyan"
            dense
    >
      <v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
      <v-toolbar-title>Application</v-toolbar-title>

    </v-app-bar>

    <v-main>
      <v-container
              class="fill-height"
              fluid
      >
      <h1>Hello World!</h1>
      </v-container>
    </v-main>

    <v-footer
            color="cyan"
            app
    >
      <v-spacer></v-spacer>

      <span class="white--text">&copy; {{ new Date().getFullYear() }}</span>
    </v-footer>
  </v-app>
</template>

<script>
  export default {
    name: 'SimpleDemo',
    props: {
      source: String,
    },
    data: () => ({
      drawer: null,
    }),
  }
</script>

上面是最简单的基础用法,接下来我们稍微复杂一点:
  1. 用数组的方法为导航栏(v-navigation-drawer) 添加连接
  2. 结合vue-router动态渲染主视图区

  • 首先回到cmd命令行状态,输入以下命令安装vue-router:

    npm install vue-router
    
  • 然后在main.js里添加:

    import VueRouter from 'vue-router'
    Vue.use(VueRouter) //启用vue-router插件
    import router from './router' //引入我们自己的路由定义表
    
    new Vue({
      vuetify:vuetify,
      router:router, //这行也可以简写为: router,
      render: function (h) { return h(App) }
    }).$mount('#app')
    
  • 在与main.js平级的目录中创建一个router.js文件:

    import VueRouter from "vue-router";
    import hello from './components/hello'
    
    const originalPush = VueRouter.prototype.push;
    VueRouter.prototype.push = function push(location) {
        return originalPush.call(this, location).catch(err => err)
    } //以上三行是为了解决相同路由报错的问题,不用深究,照原样放在此就行了
    
    export default new VueRouter({
        mode: 'history',
        routes: [
            {path: '/', component: hello},
            {path: '/msg/:msg', name: 'sayhi', component: hello},
        ]
    }) 
    /*以上代码创建了两个路由,“/” 根路由和“/msg”
       大白话解释根路由就是当我们在浏览器地址栏什么参数都不带,直接输入网址比如qq.com,页面应该跳到哪,
       第二个路由/msg,就是说当我们在网址后面加一个/msg,比如qq.com/msg,页面应该跳到哪,所谓路由就是这个意思。*/
    
  • 在 src/components 目录中创建一个hello.vue文件:

    <template>
        <h1>Hello {{ $route.params.msg}}</h1>
      <!-- 此处的$route.params.msg表示从路由/msg传递过来的参数 -->
    </template>
    
    <script>
        export default {
            name: 'hello',
    
            data: () => ({
    
            }),
        }
    /* 以上就是一个最简单的vue组件,它由template和script两部分组成,template部分表示HTML代码,
    很简单,就是显示一个H1级别的文字:Hello + 路由传过来的参数,
    JS部分其实并没有任何实质性内容,是一个空框架。 */
    </script>
    
    
  • 修改app.vue文件,清空旧的内容,将以下内容全部复制粘贴进去:

    <template>
        <v-app id="inspire">
            <v-navigation-drawer
                    v-model="drawer"
                    app
                    clipped>
                <v-list dense>
                    <v-subheader class="mt-4 grey--text text--darken-1">操作选项</v-subheader>
                    <!-- v-subheader 是组的标题 -->
                    <v-list-item
                            v-for="item in items"
                            :key="item.text"
                            @click="test(item.path)"
                            link>
                        <!-- v-for 是 vue的标准语法,item in items的 items 是一个数组,:key 表示唯一值 -->
                        <v-list-item-action>
                            <v-icon>{{ item.icon }}</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>
                                {{ item.text }}
                            </v-list-item-title>
                        </v-list-item-content>
                        <!-- v-list-item-action 是选项图标
                             v-list-item-content 是选项的内容区
                             v-list-item-title 是选项的文字
                        -->
                    </v-list-item>
                    <v-subheader class="mt-4 grey--text text--darken-1">用户选项</v-subheader>
                    <v-list>
                        <v-list-item
                                v-for="item in items2"
                                :key="item.text"
                                @click="test(item.path)"
                                link>
                            <v-list-item-avatar>
                                <!-- https://randomuser.me 是一个随机产生用户头像的趣味网站,此处从该网站获取头像图片 -->
                                <img
                                        :src="`https://randomuser.me/api/portraits/men/${item.picture}.jpg`"
                                        alt="">
                            </v-list-item-avatar>
                            <v-list-item-title v-text="item.text"></v-list-item-title>
                        </v-list-item>
                    </v-list>
    
                  <!-- 以下两个选项并没有用到数组,一般用于一些非动态的固定栏目 -->
                  <v-list-item class="mt-4" link>
                      <v-list-item-action>
                          <v-icon color="grey darken-1">mdi-plus-circle-outline</v-icon>
                      </v-list-item-action>
                      <v-list-item-title class="grey--text text--darken-1">附加选项1</v-list-item-title>
                  </v-list-item>
    
                  <v-list-item link>
                      <v-list-item-action>
                          <v-icon color="grey darken-1">mdi-cog</v-icon>
                      </v-list-item-action>
                      <v-list-item-title class="grey--text text--darken-1">附加选项2</v-list-item-title>
                  </v-list-item>
    
    
              </v-list>
          </v-navigation-drawer>
    
          <!-- v-app-bar 是顶部栏,一般放一些操作类的按钮、搜索功能等 -->
            <v-app-bar
                    app
                    clipped-left
                    color="cyan"
                    dense>
                <v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
                <!-- 此处通过修改drawer变量来决定导航栏是否隐藏,PS:当宽度被压缩的时候导航栏自动隐藏(比如手机) -->
                <v-toolbar-title>我的第一个APP</v-toolbar-title>
            </v-app-bar>
    
            <v-main> <!-- v-main是主试图,一般指与用户交互信息最频繁的区域,尺寸也最大 -->
                <v-container
                        class="fill-height"
                        fluid>
                    <router-view></router-view>
                    <!-- router-view 是路由视图,APP的所有点击变化都在此反映,一般来说路由视图约等于主视图-->
                </v-container>
            </v-main>
    
            <v-footer color="cyan" app> <!-- v-footer是最底部的页脚栏,一般用于放置版权信息之类杂项 -->
                <v-spacer></v-spacer>
                <span class="white--text">&copy; {{ new Date().getFullYear() }}</span>
            </v-footer>
        </v-app>
    </template>
    
    <script>
        export default {
            name: 'sayhi',
            props: {
                source: String,
            },
            data: () => ({
                drawer: null,
                items: [
                    {icon: 'mdi-trending-up', text: '世界', path: 'Wolrd'},
                    {icon: 'mdi-youtube-subscription', text: '太阳', path: 'Sun'},
                    {icon: 'mdi-history', text: '月亮', path: 'Moon'},
                    {icon: 'mdi-playlist-play', text: '星星', path: 'Star'},
                    {icon: 'mdi-clock', text: '地球', path: 'Earth'},
                ],
                items2: [
                    {picture: 28, text: 'Joseph',path:"约瑟夫"},
                    {picture: 38, text: 'Apple',path:"苹果"},
                    {picture: 48, text: 'Xbox',path:"叉博克斯"},
                    {picture: 58, text: 'Nokia',path:"诺基亚"},
                    {picture: 78, text: 'MKBHD',path:"爱慕凯碧爱趣帝"},
                ],
            }),
            methods: {
                test: function (path) {
                    this.$router.push({name: "sayhi", params: {msg: path}})
                }
            }
        }
    </script>
    
  • 在命令行输入: npm run serve


运行结果如下,点击导航栏不同的菜单选项,触发vue-router产生不同的参数,主视图也会跟着一起改变:

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