【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组件

vue 复制代码
<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代码

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组件

js 复制代码
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组件

vue 复制代码
<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代码

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组件

vue 复制代码
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文件,配置路由跳转逻辑

vue 复制代码
import AddBlog from './../components/AddBlog';
import ShowBlogs from './../components/ShowBlogs';
export default[
    {
        path:"/",
        component:ShowBlogs
    },
    {
        path:"/add",
        component:AddBlog
    }
]

修改App.vue,注册BlogHeader组件实现导航栏功能

vue 复制代码
<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代码

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,增加详情页路由配置

vue 复制代码
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代码,实现博客详情页

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,增加点击跳转

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请求数据

javascript 复制代码
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

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

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

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,增加删除逻辑

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,增加编辑页跳转

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,增加编辑页跳转

javascript 复制代码
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,实现博客编辑页

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>

运行效果
发布成功

整体项目结构

相关推荐
ziyue757517 分钟前
vue修改element-ui的默认的class
前端·vue.js·ui
树叶会结冰38 分钟前
HTML语义化:当网页会说话
前端·html
冰万森43 分钟前
解决 React 项目初始化(npx create-react-app)速度慢的 7 个实用方案
前端·react.js·前端框架
牧羊人_myr1 小时前
Ajax 技术详解
前端
浩男孩1 小时前
🍀封装个 Button 组件,使用 vitest 来测试一下
前端
蓝银草同学1 小时前
阿里 Iconfont 项目丢失?手把手教你将已引用的 SVG 图标下载到本地
前端·icon
布列瑟农的星空1 小时前
重学React —— React事件机制 vs 浏览器事件机制
前端
程序定小飞2 小时前
基于springboot的在线商城系统设计与开发
java·数据库·vue.js·spring boot·后端
一小池勺2 小时前
CommonJS
前端·面试