【Vue】简易博客项目跟做
项目框架搭建
1.使用vue create
快速搭建vue项目
2.使用VC Code打开新生成的项目
- 端口号简单配置
修改
vue.config.js
文件,内容修改如下
所需库安装
npm install vue-resource --save --no-fund
npm install vue-router@3 --save --no-fund
npm install axios --save --no-fund
关闭eslint检测
修改.eslintrc.js文件,注释掉
'eslint:recommended'
,并在rules中加上"vue/no-unused-components":"off"
不关闭检测,组件注册后未被使用时,会直接报错导致项目无法运行
前端网站开发
添加博客页
修改App.vue,注册
AddBlog
组件
<template>
<div id="app">
<add-blog></add-blog>
</div>
</template>
<script>
import AddBlog from './components/AddBlog'
export default {
name: 'app',
components: {
AddBlog
}
}
</script>
<style>
</style>
编写AddBlog.vue代码
<template>
<div id="add-blog">
<h2>添加博客</h2>
<form v-if="!submited">
<label>博客标题</label>
<input type="text" v-model="blog.title" required />
<label>博客内容</label>
<textarea v-model="blog.content"></textarea>
<div id="checkboxes">
<label>Vue.js</label>
<input type="checkbox" value="Vue.js" v-model="blog.categories">
<label>Node.js</label>
<input type="checkbox" value="Node.js" v-model="blog.categories">
<label>React.js</label>
<input type="checkbox" value="React.js" v-model="blog.categories">
<label>Angular4</label>
<input type="checkbox" value="Angular4" v-model="blog.categories">
</div>
<label>作者:</label>
<select v-model="blog.author">
<option v-for="author in authors" :key="author">{{ author }}</option>
</select>
<button v-on:click.prevent="post">添加博客</button>
</form>
<div v-if="submited">
<h3>添加成功</h3>
</div>
<hr>
<!-- 展示预览区域 -->
<div id="preview">
<h3>博客总览</h3>
<p>博客标题:{{ blog.title }}</p>
<p>博客内容:</p>
<p>{{ blog.content }}</p>
<p>博客分类:</p>
<ul>
<li v-for="category in blog.categories" :key="category">
{{ category }}
</li>
</ul>
<p>作者:</p>
<p>{{ blog.author }}</p>
</div>
</div>
</template>
<script>
export default {
// 测试数据网站:https://jsonplaceholder.typicode.com/posts
name: 'add-blog',
data() {
return {
blog: {
title: "",
content: "",
categories: [],
author: ""
},
authors: ["cy", "fy", "cyfy"],
submited: false
}
},
methods: {
post: function () {
// 向指定网站发送post请求
this.$http.post("https://jsonplaceholder.typicode.com/posts", {
title: this.blog.title,
body: this.blog.content,
UserId: 1
}).then(function (data) { // 接受网站返回的数据
this.submited = true;
// console.log(data);
});
}
}
}
</script>
<style scoped>
/* 针对id=add-blog下所有元素 */
#add-blog * {
box-sizing: border-box;
}
#add-blog {
margin: 20px auto;
max-width: 600px;
padding: 20px;
}
label {
display: block;
margin: 20px 0 10px;
}
input[type="text"],
textarea,
select {
display: block;
width: 100%;
padding: 8px;
}
textarea {
height: 200px;
}
#checkboxes label {
display: inline-block;
margin-top: 0;
}
#checkboxes input {
display: inline-block;
margin-right: 10px;
}
button {
display: block;
margin: 20px 0;
background: cyan;
color: fff;
border: 0;
padding: 14px;
border-radius: 4px;
font-size: 18px;
cursor: pointer;
}
#preview {
padding: 10px 20px; /* 内边距:上下边距 左右边距 */
border: 1px dotted #ccc; /* 边框设置:边框大小 边框样式 边框颜色 */
margin: 30px 0; /* 外边距:上下边距 左右边距 */
}
h3 {
margin-top: 10px; /* 上外边距 */
}
</style>
编写main.js文件,注册使用
VueResource
组件
import Vue from 'vue'
// 导入vue-resource
import VueResource from 'vue-resource'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
// 使用VueResource
Vue.use(VueResource)
new Vue({
store,
render: h => h(App),
}).$mount('#app')
运行效果
--------------------------------------------------
博客列表页
修改App.vue,注册
ShowBlogs
组件
<template>
<div id="app">
<!-- <add-blog></add-blog> -->
<show-blogs></show-blogs>
</div>
</template>
<script>
import AddBlog from './components/AddBlog'
import ShowBlogs from './components/ShowBlogs'
export default {
name: 'app',
components: {
AddBlog,ShowBlogs
}
}
</script>
<style>
</style>
编写ShowBlogs.vue代码
<template>
<div v-theme:column="'wide'" id="show-blogs">
<h1>博客总览</h1>
<input type="text" v-model="search" placeholder="搜索" />
<div class="single-blog" v-for="blog in filteredBlogs" :key="blog.id">
<h2 v-rainbow>{{ blog.title | to-uppercase }}</h2>
<article>{{ blog.body | snippet }}</article>
</div>
</div>
</template>
<script>
export default {
name: 'show-blogs',
data() {
return {
blogs: [],
search: ""
}
},
created() {
this.$http.get('https://jsonplaceholder.typicode.com/posts')
.then(function (data) {
// 截取前十条数据,赋值给blogs中
this.blogs = data.body.slice(0, 10);
})
},
computed: {
// 过滤博客,筛选出符合条件的博客
filteredBlogs: function () {
return this.blogs.filter((blog) => {
return blog.title.match(this.search) || blog.body.match(this.search);
})
}
},
filters: {
// 标题字母全部转换为大写字母
toUppercase(value) {
return value.toUpperCase();
},
// 博客内容一次仅显示100字符,后面字符用“...”代替
snippet(value) {
return value.slice(0, 100) + "...";
}
},
directives: {
// 标题字体颜色随机
"rainbow": {
bind(el, binding, value) {
el.style.color = "#" + Math.random().toString(16).slice(2, 8);
}
},
// 根据属性值赋予博客列表最大宽度
"theme": {
bind(el, binding, value) {
if (binding.value == 'wide') {
el.style.maxWidth = "1260px";
} else if (binding.value == 'narrow') {
el.style.maxWidth = "560px";
}
if (binding.arg == 'column') {
el.style.background = "#6677cc";
el.style.padding = '20px';
}
}
}
}
}
</script>
<style>
#show-blogs {
max-width: 800px;
margin: 0 auto;
}
.single-blog {
padding: 20px;
margin: 20px 0;
box-sizing: border-box;
background: #eee;
border: 1px dotted #aaa;
}
#show-blogs a {
color: #444;
text-decoration: none;
}
input[type="text"] {
padding: 8px;
width: 100%;
box-sizing: border-box;
}
</style>
运行效果
搜索
实现路由跳转
编写main.js文件,注册使用
vue-router
组件
import Vue from 'vue'
import VueResource from 'vue-resource'
import App from './App.vue'
import store from './store'
// 导入vue-router
import VueRouter from 'vue-router'
// 导入路由配置文件
import Routes from './routes/routes'
Vue.config.productionTip = false
Vue.use(VueResource)
// 使用VueRouter
Vue.use(VueRouter)
// 创建路由
const router = new VueRouter({
routes:Routes,
// 去除路径上的#号
mode:"history"
})
new Vue({
store,
render: h => h(App),
// 注册路由配置
router:router
}).$mount('#app')
编写routes.js文件,配置路由跳转逻辑
import AddBlog from './../components/AddBlog';
import ShowBlogs from './../components/ShowBlogs';
export default[
{
path:"/",
component:ShowBlogs
},
{
path:"/add",
component:AddBlog
}
]
修改App.vue,注册
BlogHeader
组件实现导航栏功能
<template>
<div id="app">
<!-- <add-blog></add-blog> -->
<!-- <show-blogs></show-blogs> -->
<blog-header></blog-header>
<router-view></router-view>
</div>
</template>
<script>
import AddBlog from './components/AddBlog'
import ShowBlogs from './components/ShowBlogs'
import BlogHeader from './components/BlogHeader'
export default {
name: 'app',
components: {
AddBlog,ShowBlogs,BlogHeader
}
}
</script>
<style>
</style>
编写BlogHeader.vue代码
<template>
<nav>
<ul>
<li>
<router-link to="/" exact>博客总览</router-link>
<router-link to="/add" exact>博客发布</router-link>
</li>
</ul>
</nav>
</template>
<script>
export default({
name:"blog-header"
})
</script>
<style scoped>
ul{
/* 去除列表前面的符号“·” */
list-style-type: none;
/* 字体居中 */
text-align:center;
margin:0;
}
li{
display: inline-block;
margin: 0 px;
}
a{
color:#fff;
text-decoration: none;
padding: 12px;
border-radius: 5px;
}
nav{
background: crimson;
padding: 30px 0;
margin-bottom: 40px;
}
.router-link-active{
background: rgba(255,255,255,0.8);
color: #444;
}
</style>
运行效果
博客详情页
修改routes.js,增加详情页路由配置
import AddBlog from './../components/AddBlog';
import ShowBlogs from './../components/ShowBlogs';
import SingleBlog from './../components/SingleBlog';
export default[
{
path:"/",
component:ShowBlogs
},
{
path:"/add",
component:AddBlog
},
{
path:"/blog/:id",component:SingleBlog
}
]
编写SingleBlog.vue代码,实现博客详情页
<template>
<div id="single-blog">
<h1>{{ blog.title }}</h1>
<article>{{ blog.body }}</article>
</div>
</template>
<script>
export default({
name:"single-blog",
data(){
return {
// 获取URL路径上的id值
id:this.$route.params.id,
blog:{}
}
},
created(){
// 请求本地JSON数据
this.$http.get('https://jsonplaceholder.typicode.com/posts/' + this.id)
.then(function(data){
this.blog = data.body;
})
}
})
</script>
<style scoped>
#single-blog{
max-width: 960px;
margin: 0 auto;
padding: 20px;
background: #eee;
border: 1px dotted #aaa;
}
</style>
修改ShowBlogs.vue,增加点击跳转
<template>
<div v-theme:column="'wide'" id="show-blogs">
<h1>博客总览</h1>
<input type="text" v-model="search" placeholder="搜索" />
<div class="single-blog" v-for="blog in filteredBlogs" :key="blog.id">
<!-- 使用router-link实现路由跳转 -->
<router-link v-bind:to="'/blog/' + blog.id">
<h2 v-rainbow>{{ blog.title | to-uppercase }}</h2>
<article>{{ blog.body | snippet }}</article>
</router-link>
</div>
</div>
</template>
运行效果
数据地址替换
因为https://jsonplaceholder.typicode.com/posts
提供的数据是死的,并不能随意修改,所以改用https://vuedemo-b1233.firebaseio.com
【别人搭建的firebase网站数据,可能会被关闭/移除】
使用axios
修改main.js,加入axios配置,替代vue-resource请求数据
import Vue from 'vue'
// import VueResource from 'vue-resource'
import App from './App.vue'
import store from './store'
import VueRouter from 'vue-router'
import Routes from './routes/routes'
import axios from 'axios'
// 全局配置
axios.defaults.baseURL = 'https://vuedemo-b1233.firebaseio.com'
Vue.config.productionTip = false
// Vue.use(VueResource)
Vue.use(VueRouter)
// 创建路由
const router = new VueRouter({
routes:Routes,
// 去除路径上的#号
mode:"history"
})
new Vue({
store,
render: h => h(App),
router:router
}).$mount('#app')
修改所有博客主页,使用axios
替代$http
修改ShowBlog.vue
<template>
<div v-theme:column="'wide'" id="show-blogs">
<h1>博客总览</h1>
<input type="text" v-model="search" placeholder="搜索" />
<div class="single-blog" v-for="blog in filteredBlogs" :key="blog.id">
<router-link v-bind:to="'/blog/' + blog.id">
<h2 v-rainbow>{{ blog.title | to-uppercase }}</h2>
<article>{{ blog.content | snippet }}</article>
</router-link>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'show-blogs',
data() {
return {
blogs: [],
search: ""
}
},
created() {
axios.get('/posts.json')
.then(function(data){
return data.data;
}).then((data) => {
var blogsArray = [];
for(let key in data){
data[key].id = key;
// 如果content为空,则将body的值赋予给content
// 这么做的原因:因为数据有很多人修改,出现了很多错误数据
if(!data[key].content){
data[key].content = data[key].body;
}
blogsArray.push(data[key]);
}
// 截取前十条数据,赋值给blogs中
this.blogs = blogsArray.slice(0, 10);
})
},
computed: {
filteredBlogs: function () {
return this.blogs.filter((blog) => {
return blog.title.match(this.search) || blog.content.match(this.search);
})
}
},
filters: {
toUppercase(value) {
return value.toUpperCase();
},
snippet(value) {
return value.slice(0, 100) + "...";
}
},
directives: {
"rainbow": {
bind(el, binding, value) {
el.style.color = "#" + Math.random().toString(16).slice(2, 8);
}
},
"theme": {
bind(el, binding, value) {
if (binding.value == 'wide') {
el.style.maxWidth = "1260px";
} else if (binding.value == 'narrow') {
el.style.maxWidth = "560px";
}
if (binding.arg == 'column') {
el.style.background = "#6677cc";
el.style.padding = '20px';
}
}
}
}
}
</script>
修改SingleBlog.vue
<template>
<div id="single-blog">
<h1>{{ blog.title }}</h1>
<article>{{ blog.content }}</article>
</div>
</template>
<script>
import axios from 'axios'
export default({
name:"single-blog",
data(){
return {
id:this.$route.params.id,
blog:{}
}
},
created(){
// 请求本地JSON数据
axios.get('/posts/' + this.id + '.json')
.then((data) => {
// 如果content为空,则将body的值赋予给content
if(!data.data.content){
data.data.content = data.data.body;
}
this.blog =data.data;
})
}
})
</script>
修改AddBlog.vue
<template>
<div id="add-blog">
<h2>添加博客</h2>
<form v-if="!submited">
<label>博客标题</label>
<input type="text" v-model="blog.title" required />
<label>博客内容</label>
<textarea v-model="blog.content"></textarea>
<div id="checkboxes">
<label>Vue.js</label>
<input type="checkbox" value="Vue.js" v-model="blog.categories">
<label>Node.js</label>
<input type="checkbox" value="Node.js" v-model="blog.categories">
<label>React.js</label>
<input type="checkbox" value="React.js" v-model="blog.categories">
<label>Angular4</label>
<input type="checkbox" value="Angular4" v-model="blog.categories">
</div>
<label>作者:</label>
<select v-model="blog.author">
<option v-for="author in authors" :key="author">{{ author }}</option>
</select>
<button v-on:click.prevent="post">添加博客</button>
</form>
<div v-if="submited">
<h3>添加成功</h3>
</div>
<hr>
<div id="preview">
<h3>博客总览</h3>
<p>博客标题:{{ blog.title }}</p>
<p>博客内容:</p>
<p>{{ blog.content }}</p>
<p>博客分类:</p>
<ul>
<li v-for="category in blog.categories" :key="category">
{{ category }}
</li>
</ul>
<p>作者:</p>
<p>{{ blog.author }}</p>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
// https://jsonplaceholder.typicode.com/posts
name: 'add-blog',
data() {
return {
blog: {
title: "",
content: "",
categories: [],
author: ""
},
authors: ["cy", "fy", "cyfy"],
submited: false
}
},
methods: {
post: function () {
axios.post("/posts.json",
this.blog)
.then( (data) => {
this.submited = true;
console.log(data);
});
}
}
}
</script>
运行效果
删除博客
修改SingleBlog.vue,增加删除逻辑
<template>
<div id="single-blog">
<h1>{{ blog.title }}</h1>
<article>{{ blog.content }}</article>
<p>作者:{{ blog.author }}</p>
<p>分类:</p>
<ul>
<li v-for="category in blog.categories" :key="category">{{ category }}</li>
</ul>
<button @click="deleteSingleBlog()">删除</button>
</div>
</template>
<script>
import axios from 'axios'
export default({
name:"single-blog",
data(){
return {
id:this.$route.params.id,
blog:{}
}
},
created(){
// 请求本地JSON数据
axios.get('/posts/'
+ this.id + '.json')
.then((data) => {
// 如果content为空,则将body的值赋予给content
if(!data.data.content){
data.data.content = data.data.body;
}
if(!data.data.categories){
data.data.categories = [];
}
this.blog =data.data;
})
},
methods:{
deleteSingleBlog(){
axios.delete('/posts/'
+ this.id + '.json').then(response =>{
console.log("删除成功");
this.$router.push({path:'/'})
})
}
}
})
</script>
运行效果:
博客编辑页
修改SingleBlog.vue,增加编辑页跳转
<template>
<div id="single-blog">
<h1>{{ blog.title }}</h1>
<article>{{ blog.content }}</article>
<p>作者:{{ blog.author }}</p>
<p>分类:</p>
<ul>
<li v-for="category in blog.categories" :key="category">{{ category }}</li>
</ul>
<button @click="deleteSingleBlog()">删除</button>
<router-link :to="'/edit/' + id">编辑</router-link>
</div>
</template>
修改router.js,增加编辑页跳转
import AddBlog from './../components/AddBlog';
import ShowBlogs from './../components/ShowBlogs';
import SingleBlog from './../components/SingleBlog';
import EditBlog from './../components/EditBlog';
export default[
{
path:"/",
component:ShowBlogs
},
{
path:"/add",
component:AddBlog
},
{
path:"/blog/:id",component:SingleBlog
},
{
path:"/edit/:id",component:EditBlog
}
]
编写EditBlog.vue,实现博客编辑页
<template>
<div id="edit-blog">
<h2>编辑博客</h2>
<form v-if="!submited">
<label>博客标题</label>
<input type="text" v-model="blog.title" required />
<label>博客内容</label>
<textarea v-model="blog.content"></textarea>
<div id="checkboxes">
<label>Vue.js</label>
<input type="checkbox" value="Vue.js" v-model="blog.categories">
<label>Node.js</label>
<input type="checkbox" value="Node.js" v-model="blog.categories">
<label>React.js</label>
<input type="checkbox" value="React.js" v-model="blog.categories">
<label>Angular4</label>
<input type="checkbox" value="Angular4" v-model="blog.categories">
</div>
<label>作者:</label>
<select v-model="blog.author">
<option v-for="author in authors" :key="author">{{ author }}</option>
</select>
<button v-on:click.prevent="edit">编辑博客</button>
</form>
<div v-if="submited">
<h3>编辑成功</h3>
</div>
<hr>
<div id="preview">
<h3>博客总览</h3>
<p>博客标题:{{ blog.title }}</p>
<p>博客内容:</p>
<p>{{ blog.content }}</p>
<p>博客分类:</p>
<ul>
<li v-for="category in blog.categories" :key="category">
{{ category }}
</li>
</ul>
<p>作者:</p>
<p>{{ blog.author }}</p>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'edit-blog',
data() {
return {
id:this.$route.params.id,
blog: {
title: "",
content: "",
categories: [],
author: ""
},
authors: ["cy", "fy", "cyfy"],
submited: false
}
},
methods: {
fetchDate(){
axios.get('/posts/' + this.id + '.json')
.then(data =>{
// 如果categories不存在,就给它赋值为空
if(!data.data.categories){
data.data.categories = [];
}
this.blog = data.data;
})
},
edit: function () {
axios.put('/posts/' + this.id + '.json',
this.blog)
.then((data) => {
this.submited = true;
});
}
},
created(){
this.fetchDate();
}
}
</script>
<style scoped>
/* 针对id=add-blog下所有元素 */
#edit-blog * {
box-sizing: border-box;
}
#edit-blog {
margin: 20px auto;
max-width: 600px;
padding: 20px;
}
label {
display: block;
margin: 20px 0 10px;
}
input[type="text"],
textarea,
select {
display: block;
width: 100%;
padding: 8px;
}
textarea {
height: 200px;
}
#checkboxes label {
display: inline-block;
margin-top: 0;
}
#checkboxes input {
display: inline-block;
margin-right: 10px;
}
button {
display: block;
margin: 20px 0;
background: cyan;
color: fff;
border: 0;
padding: 14px;
border-radius: 4px;
font-size: 18px;
cursor: pointer;
}
#preview {
/* 内边距 */
padding: 10px 20px;
/* 边框 */
border: 1px dotted #ccc;
/* 外边距 */
margin: 30px 0;
}
h3 {
margin-top: 10px;
}
</style>
运行效果
发布成功