一、vue入门基础开发—手把手教你用vue开发

(获取本节完整代码 GitHub/chizijijiadami/hand-to-hand

1、软件准备

Visual Studio Code官网
git官网
npm官网
(1)以上软件一路默认安装好后,在vscode中打开一个文件夹作为项目目录(File>Open Folder,选择一个文件夹),这里选择D盘下新建的一个projects空文件夹,选择确定后如下图:


(2)点击Terminal或者按【Ctrl+Shift+`】快捷键,打开vscode终端命令行,如下图:

(3)全局安装yarn命令管理工具,在命令行中输入以下命令:

npm install yarn -g

注意:npm执行中若遇“禁止运行脚本”问题,见文章 解决:vscode中npm运行时报“禁止运行脚本”
(4) 安装vue开发脚手架vue-cli

yarn add @vue/cli -g

安装完成后,输入“vue --version”验证一下,如下图:


注意:我们这里用yarn命令开发,习惯用npm的,见文章 yarn和npm常用开发命令对比

(5)新建一个项目“vue create website”(website为自定义项目名),一路默认按Enter

建成后,按照提示敲入命令,启动项目,如图

浏览器中输入http://localhost:8080/访问页面,至此一个简单的web项目构建好了,接下来进行下一步,路由设置。

2、路由vue-router ( vue-router官网

● 安装vue-router,vscode中方便开发,可以先【Terminal->Split Terminal】分离控制台,不想分离,要结束启动中的项目,在控制台中按【ctrl+c】停止项目,然后安装,再启动等

yarn add vue-router -S

package.json

 "dependencies": {
      "core-js": "^3.6.4",
      "vue": "^2.6.11",
  +  "vue-router": "^3.1.6"
  }

● 编写页面,新建src>pages>Index>index.vue
pages>Index>index.vue【关联:export、export default 和 import 用法详解,这一篇就够了】

<template>
  <div>Index-index</div>
</template>
<script>
export default {
    name:"IndexIndex"
}
</script>

● 写好接下来就该访问了,新建src>router>index.js
router>index.js【关联:js的 new 运算符详解】

import Vue from 'vue'
import Router from 'vue-router'
import Index from '../pages/Index/index.vue'
Vue.use(Router)
export default new Router({
    routes:[
        {
            path:'/index',
            component:Index
        }
    ]
})

mian.js 中引入路由文件

import Vue from 'vue'
import App from './App.vue'
+ import router from './router'
Vue.config.productionTip = false

new Vue({
+  router,
    render: h => h(App),
}).$mount('#app')

App.vue 添加路由渲染组件,删除示例代码

<template>
  <div id="app">
    -   <img alt="Vue logo" src="./assets/logo.png">
    -   <HelloWorld msg="Welcome to Your Vue.js App"/>
    +  <router-view></router-view>    //代表将在这里加载你的页面
  </div>
</template>
<script>
- import HelloWorld from './components/HelloWorld.vue'
export default {
    name: 'App',
-   components: {
-     HelloWorld
-   }
}
</script>

浏览器输入http://localhost:8080/#/index访问。
很容易发现默认路径http://localhost:8080/#/访问不到内容了,这时候要在路由文件中添加默认访问页配置,此时访问http://localhost:8080/#/会自动重定向到redirect中设置的路径。
router>index.js

 routes:[
      +  {
      +      path:'',
      +      redirect:'/index'
      +  },
        {
            path:'/index',
            component:Index
        }
    ]

输入未定义的路由,发现返回空白,此时应该设置一个404页面提示,404状态代表此页面不存在。
新建页面,pages>ErrorPages>404.vue
pages>ErrorPages>404.vue

<template>
  <div>您访问的页面不存在</div>
</template>

router>index.js

import Vue from 'vue'
import Router from 'vue-router'
import Index from '../pages/Index/index.vue'
+ import Error404 from '../pages/ErrorPages/404.vue'
Vue.use(Router)
export default new Router({
    routes:[
        {
            path:'',
            redirect:'/index'
        },
        {
            path:'/index',
            component:Index
        },
   +      {
   +         path:'/404',
   +         component:Error404
   +      },
   +     {
   +         path:'*',
   +         redirect:'/404'
   +    }
    ]
})

现在访问未定义的路由,发现都跳转到新编写的404页面了

3、更多开发配置详解

(1)添加其他页面
新建列表页
pages>List>index.vue

<template>
  <div>
      <p>List-index</p>
  </div>
</template>
<script>
export default {
    name:"ListIndex"
}
</script>
<style>
p{
    background-color: aqua;
}
</style>

router>index.js

import Index from '../pages/Index/index.vue'
+  import List from '../pages/List/index.vue'
import Error404 from '../pages/ErrorPages/404.vue'

        {
            path:'/index',
            component:Index
        },
+        {
+           path:'/list',
+            component:List
+        },
        {
            path:'/404',
            component:Error404
        },

App.vue

<template>
  <div id="app">
+    <header>
+      <nav><router-link to="/index">index</router-link><router-link to="/list">list</router-link></nav>
+    </header>
     <router-view></router-view>
  </div>
</template>
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
+ a{
+   margin: 0 10px;
+ }
</style>

分别访问 /index,/list 如下图可见App.vue中是页面公共部分, <header> 标签中导航<router-lint>会被渲染成<a>标签。:

添加其他页面也是同样的步骤,编写页面>设置路由>使用。

随着开发进行,页面越来越多,router/index.js中要导入的模板可能就是竖起摩天大楼了,增删都不方便,为了体验更好,下面我们就进行路由文件router/index.js中模板导入的改写,在此之前先进行项目文件路径别名设置。
添加文件,项目根路径下添加 vue.config.js
vue.config.js

const path=require('path')
const resolve=dir=>path.resolve(__dirname,dir)
module.exports={
    chainWebpack:config=>{
        config.resolve.alias
        .set("@",resolve('src'))
    }
}

router>index.js

import Vue from 'vue'
import Router from 'vue-router'
- import Index from '../pages/Index/index.vue'
- import List from '../pages/List/index.vue'
- import Error404 from '../pages/ErrorPages/404.vue'
Vue.use(Router)
+ const _import=file=>()=>import('@/pages/'+file+'.vue')
export default new Router({
    routes:[
        {
            path:'',
            redirect:'/index'
        },
        {
            path:'/index',
-           component:Index
+           component:_import('Index/index')
        },
        {
            path:'/list',
-           component:List
+           component:_import('List/index')
        },
        {
            path:'/404',
-           component:Error404
+           component:_import('ErrorPages/404')
        },
        {
            path:'*',
            redirect:'/404'
        }
    ]
})

改完以后各种访问试一下,都是正常的。其实就是把逐个导入,改成了用函数实现即时导入的形式。
有人问,如果列表页List中还需要子页面怎么设置,这就是嵌套路由了,接着看。
(2)嵌套路由
添加子页面
pages>List>index.vue

<template>
  <div>
       <p>List-index</p> 
+      <h1><router-link to="/list/detail">Detail</router-link><router-link to="/list/feature">Feature</router-link></h1>
+      <router-view></router-view>   
  </div>
</template>

pages>List>Detail>index.vue

<template>
  <div>List-Detail</div>
</template>

pages>List>Feature>index.vue

<template>
  <div>List-Feature</div>
</template>

router>index.js

        {
            path: '/list',
            component: _import('List/index'),
+            children: [
+                {
+                   path: 'detail',
+                  component: _import('List/Detail/index')
+                },
+                {
+                    path: 'feature',
+                    component: _import('List/Feature/index')
+                }
+            ]
        },

切换查看效果,除了样子像tab标签页,浏览器地址栏路由也是切换的,那么如果我们并不想要嵌套路由,只是一个tab切换,应该怎么做呢?
(3) tab标签页切换页
page>List>index.vue

<template>
  <div>
      <p>List-index</p> 
-      <h1><router-link to="/list/detail">Detail</router-link><router-link to="feature">Feature</router-link></h1>
-      <router-view></router-view> 
+      <ul><li @click="changeTab('detail')">detail</li><li @click="changeTab('feature')">feature</li></ul>  
+      <div v-show="tab=='detail'"  class="detail">detail-tab</div>
+      <div v-show="tab=='feature'"  class="feature">feature-tab</div>
  </div>
</template>
<script>
export default {
    name:"ListIndex",
+    data(){
+        return {
+            tab:'detail'
+       }
+    },
+    methods:{
+        changeTab(tab){
+            this.tab=tab
+        }
+    }
}
</script>

这样就是tab标签页切换效果了,此处的v-show与v-if的区别就是,用户频繁切换用v-show比较好,详见官方解释v-if vs v-show
如果有多个tab标签,并且每个标签都有不少内容,很容易造成List>index.vue页面过于臃肿,那么此时我们就需要提到组件了。
(4)组件
● 新建引入
这里我们用之前的pages>List>Detail>index.vue和pages>List>Feature>index.vue两个,也可以自己新建。
pages>List>index.vue

<template>
  <div>
      <p>List-index</p> 
      <ul><li @click="changeTab('detail')">detail</li><li @click="changeTab('feature')">feature</li></ul>  
-     <div v-show="tab=='detail'"  class="detail">detail-tab</div>
-     <div v-show="tab=='feature'"  class="featur">feature-tab</div>
+     <div v-show="tab=='detail'"  class="detail">
+          <detail></detail>        //C、使用
+     </div>
+     <div v-show="tab=='feature'"  class="feature">
+          <feature></feature>
+     </div>
  </div>
</template>

<script>
+ import Detail from './Detail/index.vue'      //A、引入要用组件
+ import Feature from './Feature/index.vue'
export default {
    name:"ListIndex",
+    components:{
+        Detail,           //B、注册
+        Feature
+    },
    data(){
        return {
            tab:'detail'
        }
    },
    methods:{
        changeTab(tab){
            this.tab=tab
        }
    }
}
</script>

步骤就是新建vue模板页>页面中import引入>components中注册>页面中使用,单纯的使用就是以上的A、B、C三步走。
● 传值——父传子
父组件通过设置绑定子组件的属性传值到子组件中,子组件中中props属性接收
pages>List>index.vue

      <div v-show="tab=='detail'"  class="detail">
-          <detail></detail>
+          <detail :deliveryValue="toChildren"  :toChildrenObj="toChildrenObj" :toChildrenArr="toChildrenArr"></detail>
      </div>

    data(){
        return {
            tab:'detail',
+           toChildren:'传给子组件咯',
+            toChildrenObj:{
+                name:'test',
+                age:12
+            },
+            toChildrenArr:[
+                {item:'1'},
+                {item:'2'}
+            ]
        }
    },

pages>List>Detail>index.vue

<template>
- <div>List-Detail</div>
+  <div>
+      <div>List-Detail</div>
+      <p>{{deliveryValue}}</p>
+      <p>{{toChildrenObj}}</p>
+      <p>{{toChildrenArr}}</p>
+  </div>
</template>

+ <script>
+ export default {
+    props:['deliveryValue','toChildrenObj','toChildrenArr']
+ }
+ </script>

子组件中用来接收的值名就是父组件中绑定时“:”后面的属性名。父子组件传值用名可以一样,但是在子组件 props 中接收过的值相当于是声明,是不可以在子组件的data中再次声明的,子组件中如果下面这么写:

    props:['deliveryValue','toChildrenObj','toChildrenArr'],
    data(){
        return {
            deliveryValue:'123',     报错,不可以
            test:{
                deliveryValue:"123"    正常,可以
            }
        }
    }

更多见官网通过-Prop-向子组件传递数据
还有个样式问题,发现在 List/Detail/index.vue 中的 <p> 标签样式背景色是 List/index.vue 中设置生效的,原来在<style>中设置的样式是在全局生效的,若想只在此模板中生效需要加 “scoped”,如下:
pages>List>index.vue

- <style>
+ <style scoped>
p{
    background-color: aqua;
}
</style>

保存观察浏览器,发现只有List/index.vue中的<p>标签背景色改变了,那么想在List/index.vue中设置子组件中<p>标签的样式就需要另写一个<style>标签不加 scoped ,还有一个方法如下:

<style scoped>
p{
    background-color: aqua;
}
+ .detail >>> p{      或者将>>>替换成 /deep/ 也行
+    background-color: red;
+ }
</style>

● 传值——子传父
page>List>index.vue

      <div v-show="tab=='feature'" class="feature">
-          <feature></feature>
+          <feature @changeTab="changeTab"></feature>
      </div>

pages>List>Feature>index.vue

 - <div> List-Feature</div>
 + <div>
 +    List-Feature
 +    <button @click="$emit('changeTab','detail')">切换到 Detail tab</button>
 + </div>

可以看到传事件与传值除了“:”换成“@”其他形式是一样,只不过在子组件中触发的时候不要接收,直接用 $emit(name,[]param]) 去触发就好了。
(5)资源引入
● 图片引入
新建src>assets>images文件夹,放一张图片进去,在src>pages>Index>index.vue中引入, ~@ 指代根路径src。

src>pages>Index>index.vue

<template>
- <div>Index-index</div>
+  <div>
+    <p>Index-index</p>
+    <img src="~@/assets/images/jerry.png" alt="" srcset="">
+  </div>
</template>

关于 svg 图片批量导入的问题,我们在后续的稳重后台模板开发中说明。
● 样式引入
新建src>assets>styles文件夹,再新建两个样式文件,随意写些样式文件,在main.js中引入


main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false

+ import './assets/styles/reset.css'
+ import './assets/styles/style.css'

还可以在组件页面中引入,新建 src>assets>styles>component.css,
src>assets>styles>component.css

p.index-p{
    color: red;
}

src>pages>Index>index.vue

<template>
  <div>
-    <p>Index-index</p>
+    <p class="index-p">Index-index</p>
     <img src="~@/assets/images/jerry.png" alt="" srcset="">
  </div>
</template>
<script>
export default {
    name:"IndexIndex"
}
</script>
+ <style>
+ @import '~@/assets/styles/component.css'
+ </style>

为了方便样式书写,这里推荐结合css的预处理器stylus编写项目,详见拓展 vue实践1.1 企业官网——prerender-spa-plugin预渲染 第2部分结合stylus开发使用说明。

4、打包部署

(1)基本打包,如下图, yarn build 进行打包。

完成后发现,根目录下多了 dist 文件夹,进去访问 index.html 文件,发现浏览器中一片空白,这是因为打包时未设置资源的访问路径。
vue.config.js

const path=require('path')
const resolve=dir=>path.resolve(__dirname,dir)
module.exports={
+    publicPath:'./',
     chainWebpack:config=>{
        config.resolve.alias
        .set("@",resolve('src'))
     }
}

重新打包 yarn build 访问,一切正常。把dist文件夹发给后端,部署服务器就可以了。至此,基本的开发打包步骤已完成,下面进行一些开发打包基本配置的说明。
(2)打包基本配置
开发、测试和生产环境
大多时候我们在开发、测试和生产这三个场景中有同一个变量需要不同值的需求,这时候我们可以建立三个对应的环境文件去解决。
开发 .env.development

// 开发 环境配置 
// 变量必须以VUE_APP开头,会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中
//代码中可以通过 process.env.VUE_APP_BASE_API 访问
// NODE_ENV 和 BASE_URL 是两个特殊变量,在代码中始终可用

NODE_ENV = 'development'
VUE_APP_BASE_API = 'http://192.168.0.1'

测试 .env.testing

// 测试 环境配置 
// 变量必须以VUE_APP开头,会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中
//代码中可以通过 process.env.VUE_APP_BASE_API 访问
// NODE_ENV 和 BASE_URL 是两个特殊变量,在代码中始终可用

NODE_ENV = 'testing'
VUE_APP_BASE_API = 'http://192.168.0.200'

生产 .env.production

// 生产 环境配置 
// 变量必须以VUE_APP开头,会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中
//代码中可以通过 process.env.VUE_APP_BASE_API 访问
// NODE_ENV 和 BASE_URL 是两个特殊变量,在代码中始终可用

NODE_ENV = 'production'
VUE_APP_BASE_API = 'http://192.168.0.100'

变量的使用在项目中用 process.env 可以取到,在启动或者打包中使用到不同配置,则需要改写package.json文件,package.json 文件中scripts 内就是 yarn 可以运行的命令,修改完package.json文件后,需要重新启动项目生效,此时 yarn serve 命令就是运行的 vue-cli-service serve --mode development。
package.json

  "scripts": {
-    "serve": "vue-cli-service serve",
-    "build": "vue-cli-service build",
+    "serve": "vue-cli-service serve --mode development",
+    "serve-pro": "vue-cli-service serve --mode production",
+    "serve-test": "vue-cli-service serve --mode testing",
+    "build": "vue-cli-service build --mode production",
+    "build-dev": "vue-cli-service build --mode development",
+    "build-test": "vue-cli-service build --mode testing",
    "lint": "vue-cli-service lint"
  },

在src>pages>Index>index.vue中试一下
src>pages>Index>index.vue

export default {
    name:"IndexIndex",
+    created(){
+      console.log(process.env.VUE_APP_BASE_API,'输出VUE_APP_BASE_API');     
+    }
}

在浏览器中按【F12】可以看到控制台中看到,自定义的变量可以正常取用。


● yarn lint
上面改写时,发现package.json文件scripts中有 lint 这个命令,这是js代码检测工具EsLint的使用,一般在代码提交前使用此命令来检查错误、规范代码等,具体内容见 ESLint中文官网

● 生产环境去console
项目开发中我们常常用控制台 console 输出各种数据,如果发布到生产环境不太好,但是每次发布删掉又很麻烦,那么我们可以这么设置。
安装去console插件 babel-plugin-transform-remove-console

yarn add babel-plugin-transform-remove-console -D

babel.config.js

+ const plugins = ["@vue/babel-plugin-transform-vue-jsx"]
+ // 生产环境移除console
+ if(process.env.NODE_ENV === 'production') {
+   plugins.push("transform-remove-console")
+ }
module.exports = {
 presets: [
   '@vue/cli-plugin-babel/preset'
 ],
+  plugins:plugins
}

以生产环境模式运行一下,发现控制台空空如也。

yarn serve-pro

(3)拓展——自己搭建后端服务器,进行项目部署
● 安装
这里我们用致力于让天下没有难配的服务器环境phpStudy集成环境 phpStudy官网,可以进行傻瓜式的搭建。下载解压,点击安装文件安装



● 访问,不修改端口的话,直接启动访问就行
因为80端口经常被占用这里我们修改一下默认配置,注意不冲突不修改也行



修改确认后,启动后,在下方网站列表里可以看到这个服务器,点击管理,选择打开网站

发现显示无法访问,那是因为我们修改过访问端口,在地址栏里加入我们自己修改的端口就行,这是默认的页面。

● 打包发布
项目中打包 yarn build,发现生成了dist文件夹,这就是我们可以发布的网站内容了

yarn build

phpStudy网站>管理>打开根目录,删除WWW下的文件,将我们dist文件中的文件放进去,刷新浏览器访问,就访问到我们的网站了。




通常这一步是由后端操作,作为对自己有要求的前端,我们希望前端自己主动掌握这些基本的后端知识。
至此,我们完成了网站的开发到发布的所有步骤。
状态管理,权限管理,多页,CSS预处理,SSR等我们后续介绍。

感谢阅读,喜欢的话点个赞吧:)
更多内容请关注后续文章。。。

二、vue+ElementUI开发后台管理模板—布局
三、vue+ElementUI开发后台管理模板—功能、资源、全局组件
四、vue+ElementUI开发后台管理模板—方法指令、接口数据
五、vue+ElementUI开发后台管理模板—精确到按钮的权限控制

vue3 + vite + ElementPlus开发后台管理模板

vue实践1.1 企业官网——prerender-spa-plugin预渲染

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

推荐阅读更多精彩内容