Laravel 5.8 + Nuxt.js: Quick start
Bài đăng này đã không được cập nhật trong 5 năm
- Ở bài viết này ta sẽ không đi sâu vào việc giới thiệu, giải thích Nuxt.js là gì? hay tại sao nên dùng Nuxt.js? Ưu nhược điểm của nó so với những thằng khác là như thế nào?
- Bài này ta sẽ bắt tay nhanh vào việc thực hiện 1 simple project quản lý users sự dụng
Laravel 5.8
làm server-side viết api vàNuxt.js
làm client-side render ra view. - Tất nhiên cần build một project laravel sau đó thêm Nuxt.js vào để xử lý phía client, ở đây ta có thể hiểu là server laravel và server Nuxt.js là hoàn toàn độc lập với nhau.
I. Tạo project Laravel
- Ta sử dụng phiên bản laravel 5.8 mới nhất tại thời điểm viết bài.
- Bước 1: Tạo project laravel bằng 1 trong 2 lệnh sau:
laravel new
hoặccomposer create-project
- Bước 2: Kết nối database và chạy migrate
php artisan migrate
- Bước 3: Chạy lệnh
php artisan make:auth
để tạo mẫu signup, login, logout.
II. Tạo API thêm sửa xóa users
-
Đây là phần sử lý CRUD chính bên phía server laravel
-
Tạo controller app/Http/Controllers/UsersController.php với nội dung như sau:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\User; class UserController extends Controller { public function index() { $users = User::all(); return response()->json($users); } public function show($id) { $user = User::find($id); return response()->json($user); } public function update(Request $request, $id) { $data = $request->only([ 'name', 'email', ]); try { $user = User::find($id); $user->update($data); $result = [ 'message' => true, ]; } catch (Exception $e) { $result = [ 'message' => false, ]; } return response()->json($result); } public function store(Request $request) { $data = $request->only([ 'name', 'email', 'password', ]); $data['password'] = bcrypt($data['password']); try { User::create($data); $result = [ 'message' => true, ]; } catch (Exception $e) { $result = [ 'message' => false, ]; } return response()->json($result); } public function destroy($id) { try { $user = User::find($id); $user->delete(); $result = [ 'status' => true, ]; } catch (Extention $e) { $result = [ 'status' => false, ]; } return response()->json($result); } }
-
Sau đó ta cần sửa file routes/api.php để chỉ định url dẫn đến UsersController.php
Route::get('users', 'UserController@index'); Route::get('users/{user}', 'UserController@show'); Route::put('users/{user}', 'UserController@update'); Route::post('users', 'UserController@store'); Route::delete('users/{user}', 'UserController@destroy');
-
Đến đây khi ta start server với câu lệnh
php artisan serve
thì mặc định server được khởi tạo ở cổng 8000 với url http://localhost:8000 -
Ví dụ api lấy ra danh sách users sẽ được gọi như sau:
-
OK, phần API đến đây là ngon lành, với api get ta có thể test như trên, còn với các api create, update, delete thì ta có thể dùng Postman.
- Lưu ý: đây là project demo đơn giản để ta hiểu sự kết hợp giữa laravel và Nuxt.js nên tạm thời ta không để authenticate, cũng như validate cho phần API này
III. Bắt đầu với Nuxt.js
Nuxt.js
được phát triển dựa trên trên Vue.js và Node.js nên nếu bạn đã có kiến thức sẵn về Vue.js và Node.js thì thật là tuyệt vời, tiếp cận Nuxt.js sẽ rất dễ dàng với bạn. Nhưng nếu bạn mới chỉ tiếp cận thì cũng không sao, ta sẽ bắt đầu từ trang chủ Nuxt.js từng bước một:- Cài đặt Nuxt.js framework
$ sudo npx create-nuxt-app <project-name> or $ sudo yarn create nuxt-app <project-name>
- Lưu ý bạn phải cài đặt npx trước, từ phiên bản
npm
>= 5.2.0 thì mặc địnhnpx
đã được cài đặt sẵn rồi. - Sau khi chạy lệnh cài đặt trên, hệ thống sẽ hỏi ta 1 vài thông tin cơ bản như, project name, description, server,.. nếu muốn thay đổi thì chỉ việc nhập vào, còn không thì cứ enter nhé:
> Generating Nuxt.js project in /home/framgia/projects/vangiang/nuxt-client ? Project name nuxt-client ? Project description My laudable Nuxt.js project ? Use a custom server framework none ? Choose features to install (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Use a custom UI framework bootstrap ? Use a custom test framework none ? Choose rendering mode Universal ? Author name giangnv-0040 ? Choose a package manager yarn To get started: cd nuxt-client yarn run dev To build & start for production: cd nuxt-client yarn run build yarn start
- Sau khi cài đặt xong ta sẽ có cấu trúc thư mục như sau:
. ├── assets │ └── README.md ├── components │ ├── Logo.vue │ └── README.md ├── layouts │ ├── default.vue │ └── README.md ├── middleware │ └── README.md ├── nuxt.config.js ├── package.json ├── pages │ ├── index.vue │ └── README.md ├── plugins │ └── README.md ├── README.md ├── static │ ├── favicon.ico │ └── README.md ├── store │ └── README.md └── yarn.lock
- Để khởi chạy ứng dụng ta chạy lệnh
yarn run dev
. Và kết quả thu được: - Tiếp đến ta sẽ quan tâm đến thư mục pages, đây là thư mục chứa các file view, đồng thời việc đặt tên, đường dẫn cho file trong này cũng sẽ quy định route, ta sẽ giải thích bằng ví dụ bên dưới, ta sẽ tạo cái file view với đuôi
.vue
bên trong thư mục pages như sau:pages/ ├── index.vue ├── README.md └── users ├── create.vue ├── _id │ └── edit.vue └── index.vue
- Với việc bố trí đường dẫn, và đặt tên như trên ta sẽ có 3 route:
- Index: http://localhost:3000/users
- Edit: http://localhost:3000/users/1/edit/
- Create: http://localhost:3000/users/create
- Cụ thể ta có thể xem route được render ra tại .nuxt/route.js, thư mục .nuxt này sẽ được tự động sinh ra khi ta start server
export function createRouter() { return new Router({ mode: 'history', base: decodeURI('/'), linkActiveClass: 'nuxt-link-active', linkExactActiveClass: 'nuxt-link-exact-active', scrollBehavior, routes: [{ path: "/users", component: _7038f74a, name: "users" }, { path: "/users/create", component: _82cb8698, name: "users-create" }, { path: "/users/:id/edit", component: _55dfcb77, name: "users-id-edit" }, { path: "/", component: _8b87105e, name: "index" }], fallback: false }) }
- List Users:
- Ta sẽ update file pages/users/index.vue như sau:
<template> <div class="container"> <table class="table table-striped table-bordered"> <thead class="thead-dark"> <tr> <th scope="col" width='5%'>#</th> <th scope="col">Name</th> <th scope="col">Email</th> <th scope="col">Action</th> </tr> </thead> <tbody> <tr v-for="user in users" :key="user.id"> <td width="5%">{{ user.id }}</td> <td>{{ user.name }}</td> <td>{{ user.email }}</td> <td> <nuxt-link :to="'users/' + user.id + '/edit/'" class="btn btn-primary">Edit</nuxt-link> <a href="#" class="btn btn-danger" @click="del(user.id)">Del</a> </td> </tr> </tbody> </table> <nuxt-link to="/users/create" class="btn btn-primary">Create User</nuxt-link> </div> </template> <script> export default { async fetch ({ store, params }) { await store.dispatch('user/getUsers'); }, computed: { users () { return this.$store.state.user.users } }, methods: { async del(id) { await this.$store.dispatch('user/deleteUser', id); this.$store.dispatch('user/getUsers'); }, } } </script>
- File
index.vue
trên có gọi đến hàmgetUser
trong fileuser.js
trong thư mục store, cụ thể:import { callApiFetch, callApiAdd, callApiEdit, callApiDelete, callApiShow } from '../api/user.js'; const FETCH = 'fetch'; const SHOW = 'show'; const EDIT = 'edit' export const state = () => ({ users: [], user: {}, }) export const actions = { async getUsers({ commit }, data = {}) { let response = await callApiFetch(data); return commit(FETCH, { users: response.data }); }, async addUser({ commit }, data = {}) { let response = await callApiAdd(data); return response; }, async getUser({ commit }, data = {}) { let response = await callApiShow(data); return commit(SHOW, { user: response.data }); }, async editUser({ commit }, data) { let response = await callApiEdit(data.id, data); return response; }, async deleteUser({ commit }, data) { let response = await callApiDelete(data) return response; }, } export const mutations = { [FETCH](state, { users }) { return state.users = users; }, [SHOW](state, { user }) { return state.user = user; }, [EDIT](state, { user }) { return state.user = user; }, }
- Trong file
user.js
này có import file api/user.js (thư mục api là mình tự tạo ra, mặc định nuxt.js không có):import axios from "./config"; export function callApiFetch(params = {}) { return axios.get('/users', { params }); } export function callApiAdd(params) { return axios.post('/users', params) .then(response => response) .catch(error => error) } export function callApiEdit(id, params) { return axios.put(`/users/${id}`, params) .then(response => response) .catch(error => error); } export function callApiDelete(id) { return axios.delete(`/users/${id}`) .then(response => response) .catch(error => error); } export function callApiShow(id) { return axios.get(`/users/${id}`) .then(response => response) .catch(error => error); }
- và file
config.js
// api/config.js import axios from "axios" const apiUrl = 'http://localhost:8000/api' axios.defaults.baseURL = apiUrl; export default axios;
- và file
- Ở đây khi ta truy cập vào trang user list, 1 apt fetch được gửi đi với cú pháp:
await store.dispatch('user/getUsers');
, lệnh này sẽ gọi đến functiongetUsers
trong fileuser.js
trong thư mục store, sau đó sẽ gán response trả về vào mảngusers
trongstate
. - Tiếp đến đoạn code sau sẽ thực hiện việc lấy data từ state để trả lại biến
users
sử dụng trên viewcomputed: { users () { return this.$store.state.user.users } },
- Phần xóa user thì được xử lý ở đoạn code này:
methods: { async del(id) { await this.$store.dispatch('user/deleteUser', id); this.$store.dispatch('user/getUsers'); }, }
- Khi click vào nút
@click="del(user.id)"
methoddel
sẽ được gọi đến, sau đó gọi sang methoddeleteUser
trong store/user.js, rồi gọi sang apicallApiDelete
bên api/user.js. Sau khi xóa xong thì 1 api fetch tương tự như trên lại được gọi lại để cập nhật trạng thái user listthis.$store.dispatch('user/getUsers');
- Phần create user và update user ta cũng có xử lý tương tự như bên dưới:
// page/users/create.vue <template> <section class="container"> <div class="card"> <div class="card-header"> Create User </div> <div class="card-body"> <form @submit="createUser($event)"> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" id="name" placeholder="Enter Name" v-model="user.name"> </div> <div class="form-group"> <label for="email">Email address</label> <input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email" v-model="user.email"> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" class="form-control" id="password" placeholder="Enter password" v-model="user.password"> </div> <button type="submit" class="btn btn-primary">Submit</button> <nuxt-link to="/users" class="btn btn-primary">Cancel</nuxt-link> </form> </div> </div> </section> </template> <script> export default { data() { return { user: {email: null, name: null, password: null}, } }, methods: { async createUser(event) { event.preventDefault(); let response = await this.$store.dispatch('user/addUser', this.user); if (response && response.data && response.data.message) { this.$router.push({name: 'users'}); } else { alert('Create error'); } }, } } </script>
// page/users/_id/edit.vue <template> <section class="container"> <div class="card"> <div class="card-header"> Edit User </div> <div class="card-body"> <form @submit="updateUser($event)"> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" id="name" placeholder="Enter Name" v-model="userData.name"> </div> <div class="form-group"> <label for="email">Email address</label> <input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email" v-model="userData.email"> </div> <button type="submit" class="btn btn-primary">Submit</button> <nuxt-link to="/users" class="btn btn-primary">Cancel</nuxt-link> </form> </div> </div> </section> </template> <script> export default { async fetch ({ store, route }) { await store.dispatch('user/getUser', route.params.id); }, data() { return { userData: {...this.$store.state.user.user}, } }, methods: { async updateUser(event) { event.preventDefault(); let response = await this.$store.dispatch('user/editUser', this.userData); if (response && response.data && response.data.message) { this.$router.push({name: 'users'}); } else { alert('Update error'); } }, } } </script>
Tài liệu tham khảo
All rights reserved