使用 Laravel 和 Vue 创建单页应用程序

凭借其干净的语法和表现力,以及许多其他品质,Laravel是开发人员使用的最受欢迎的PHP框架之一。

在 Laravel UI 推出之前,它的主要功能之一是默认支持 Vue.js从 Laravel v5.3 到 v6。Vue 是一个现代的 JavaScript 前端框架,用于创建用户界面。

为什么拉拉维尔和 Vue 在一起很好?

以下是将Laravel与Vue一起使用为您的项目创建全栈工作流程的一些主要优势:

  • 源代码被合并到一个项目中,而不是为后端和前端提供单独的项目

  • 设置和配置简单

  • 单个部署可以同时处理两个框架

什么是水疗?(单页申请)

单页应用程序(简称 SPA)将Windows10怎么进入BIOS设置?详细八个步骤图文教程新数据从 Web 服务器动态加载到网页,而无需刷新整个页面。

使用SPA的流行网站的例子包括 gmail.com 和 youtube.com - 换句话说,SPA在很大程度上无处不在。您可能每天使用的大多数管理仪表板都是使用 SPA 创建的。

水疗的好处:

  • 用户体验更灵活

  • 在浏览器中缓存数据

  • 快速加载时间

SPA的缺点:

  • 可能会损害 SEO(搜索引擎优化)

  • 潜在的安全问题

  • 消耗大量浏览器资源

项目设置

这篇文章将演示如何开发一个允许用户注册帐户和添加任务的待办事项应用程序。

在本教程中,使用的是 Laravel 9,它需要 PHP 8.1 和 Vue 3;我们还需要安装 PHP 和 NGINX。

让我们从以下命令开始:

composer create-project --prefer-dist laravel/laravel laravel-vue-demo

接下来,我们将安装 JavaScript 依赖项。

npm install

我们必须先安装一些软件包,然后才能将 Vue 添加到我们的项目中。

此外,必须安装 plugin-vue,小米手机怎么测试硬件是不是坏了?小米手机如何自己检测?因为 Laravel 9 附带了 Vite,而不是 webpack-mix,后者是 JavaScript 的 Laravel 捆绑器。现在让我们这样做:

npm install vue@next vue-loader@next @vitejs/plugin-vue

打开调用的文件并添加到配置中:vite.config.js``vue()

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue'
​
export default defineConfig({
    plugins: [
        vue(),
        laravel([
            'resources/css/app.css',
            'resources/js/app.js',
        ]),
    ],
});

编辑 Vue 3 应用引导程序的文件和代码片段:app.js

require('./bootstrap');
​
import {createApp} from 'vue'
​
import App from './App.vue'
​
createApp(App).mount("#app")

创建一个名为的文件并添加以下内容:App.vue

<template>
  <h1> Hello, Vuejs with Laravel </h1>
</template>
<script>
export default {
  setup() {
​
   }
}
</script>

最后,打开位于文件夹中的文件并添加以下内容:welcome.blade.php``resources/views

<!DOCTYPE html>
<html>
<head>
 ....
        @vite('resources/css/app.css')
</head>
<body>
  <div id="app"></div>
  @vite('resources/js/app.js')
</body>
</html>

要预览我们的应用程序,趣知笔记我们需要在两个不同的终端/命令行上启动我们的 Vue 应用程序和 Laravel 服务器:

npm run dev
​
​
php artisan serve

要构建我们的待办事项应用程序,我们需要创建更多文件。Vue 将创建多个页面,主要是:

  • 登录页面

  • 注册页面

  • 主页

要与Laravel端点通信,我们需要安装Axios:

npm install axios

Vue 路由

使用 vue-router 包,趣知笔记网站地图Vue 中可以使用各种路由策略;这些策略也称为历史记录模式。

当用户请求类似路由时(刷新页面时将返回 404 错误),我们可以依靠 Laravel 来检测任何回退路由,然后提供包含我们应用程序的 Blade 文件。http://localhost:8000/home

因此,我们将使用 HTML5 模式:

Route::get('/{vue_capture?}', function() {
    return view('welcome');
})->where('vue_capture', '[\/\w\.-]*');
import {createRouter, createWebHistory} from 'vue-router';

const router = createRouter({
    history: createWebHistory(),
    routes: [
        {
            path: '/',
            component: () => import('./pages/Login.vue')
        },
        {
            path: '/register',
            component: () => import('./pages/Register.vue')
        },
        {
            path: '/home',
            component: () => import('./pages/Home.vue')
        }
    ],
})

由于项目的简单性,我们本质上是使用 Laravel Sanctum 处理登录页面的身份验证,然后将我们的令牌保存在本地存储中。

为了使其他请求成功,令牌将连接到标头,这将允许发出请求的用户由 Laravel 识别。

以下是我们的登录页面:

以下是我们的注册页面:

最后,以下是两者的相关代码块:

<!--Login.vue-->
<template>
    <div class="mx-auto w-4/12 mt-10 bg-blue-200 p-4 rounded-lg">
        <div
            class="bg-white shadow-lg rounded-lg px-8 pt-6 pb-8 mb-2 flex flex-col"
        >
            <h1 class="text-gray-600 py-5 font-bold text-3xl"> Login </h1>
            <ul class="list-disc text-red-400" v-for="(value, index) in errors" :key="index" v-if="typeof errors === 'object'">
                <li>{
  
  {value[0]}}</li>
            </ul>
            <p class="list-disc text-red-400" v-if="typeof errors === 'string'">{
  
  {errors}}</p>
            <form method="post" @submit.prevent="handleLogin">
            <div class="mb-4">
                <label
                    class="block text-grey-darker text-sm font-bold mb-2"
                    for="username"
                >
                    Email Address
                </label>
                <input
                    class="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker"
                    id="username"
                    type="text"
                    v-model="form.email"
                    required
                />
            </div>
            <div class="mb-4">
                <label
                    class="block text-grey-darker text-sm font-bold mb-2"
                    for="password"
                >
                    Password
                </label>
                <input
                    class="shadow appearance-none border border-red rounded w-full py-2 px-3 text-grey-darker mb-3"
                    id="password"
                    type="password"
                    v-model="form.password"
                    required
                />
            </div>
            <div class="flex items-center justify-between">
                <button
                    class="bg-blue-500 hover:bg-blue-900 text-white font-bold py-2 px-4 rounded"
                    type="submit"
                >
                    Sign In
                </button>
                <router-link
                    class="inline-block align-baseline font-bold text-sm text-blue hover:text-blue-darker"
                    to="register"
                >
                    Sign Up
                </router-link>
            </div>
            </form>
        </div>
    </div>
</template>
export default {
    setup() {
        const errors = ref()
        const router = useRouter();
        const form = reactive({
            email: '',
            password: '',
        })
        const handleLogin = async () => {
            try {
                const result = await axios.post('/api/auth/login', form)
                if (result.status === 200 && result.data && result.data.token) {
                    localStorage.setItem('APP_DEMO_USER_TOKEN', result.data.token)
                    await router.push('home')
                }
            } catch (e) {
                if(e && e.response.data && e.response.data.errors) {
                    errors.value = Object.values(e.response.data.errors)
                } else {
                    errors.value = e.response.data.message || ""
                }
            }
        }

        return {
            form,
            errors,
            handleLogin,
        }
    }
}

Vue 视图/页面处理所有待办事项,例如创建、删除、更新和列出待办事项。所有操作都使用用户令牌向端点发出请求,以便通过 Axios 进行授权。Home

让我们看一下它们的外观,然后是相关的片段:

<!-- Home -->
<template>
    <div class="w-6/12 p-10 mx-auto">
        <div class="flex justify-between">
            <h1 class="text-2xl"> Todo </h1>
            <span class="capitalize">Welcome {
  
  { user && user.name }}, <button
                class="text-orange-500 underline hover:no-underline rounded-md"
                @click="handleLogout">Logout</button></span>
        </div>
        <input type="text" class="p-2 w-64 border rounded-md" v-model="todo" placeholder="Enter your todo"/>
        <button class="bg-blue-600 text-white px-5 py-2 rounded-md ml-2 hover:bg-blue-400" @click="addTodo">Add</button>
        <Loader v-if="isLoading"/>
        <ul class="border-t mt-3 cursor-pointer">
            <li :class="`py-3 border-b text-gray-600 ${val.has_completed ? 'line-through' : ''}`"
                v-for="(val, idx) in todos" :key="idx">
                <input type="checkbox" :checked="val.has_completed" @click="checked(idx)"/>
                <span @click="checked(val, idx)" class="pl-3">{
  
  { val.title }} </span>
                <button class="float-right bg-red-400 px-2 text-white font-bold rounded-md hover:bg-red-600"
                        @click="deleteTodo(val, idx)">&times;
                </button>
            </li>
        </ul>
    </div>
</template>
setup() {
    const todo = ref('')
    const todos = ref([])
    const user = ref()
    const isLoading = ref()

    let router = useRouter();
    onMounted(() => {
        authentication()
        handleTodos()
    });

    const authentication = async () => {
        isLoading.value = true
        try {
            const req = await request('get', '/api/user')
            user.value = req.data
        } catch (e) {
            await router.push('/')
        }
    }

    const handleTodos = async () => {
        try {
            const req = await request('get', '/api/todos')
            todos.value = req.data.data
        } catch (e) {
            await router.push('/')
        }
        isLoading.value = false
    }

    const handleNewTodo = async (title) => {
        try {
            const data = {title: title}
            const req = await request('post', '/api/todos', data)
            if (req.data.message) {
                isLoading.value = false
                return alert(req.data.message)
            }
            todos.value.push(req.data.data)
        } catch (e) {
            await router.push('/')
        }
        isLoading.value = false
    }

    const handleLogout = () => {
        localStorage.removeItem('APP_DEMO_USER_TOKEN')
        router.push('/')
    }

    const addTodo = () => {
        if (todo.value === "") {
            return alert("Todo cannot be empty");
        }
        isLoading.value = true
        handleNewTodo(todo.value)
        todo.value = ""
    }

    const checked = async (val, index) => {
        try {
            const data = {has_completed: !val.has_completed}
            const req = await request('put', `/api/todos/${val.id}`, data)
            if (req.data.message) {
                isLoading.value = false
                return alert(req.data.message)
            }
            todos.value[index].has_completed = !val.has_completed
        } catch (e) {
            await router.push('/')
        }
        isLoading.value = false
    }

    const deleteTodo = async (val, index) => {
        if (window.confirm("Are you sure")) {
            try {
                const req = await request('delete', `/api/todos/${val.id}`)
                if (req.data.message) {
                    isLoading.value = false
                    todos.value.splice(index, 1)
                }
            } catch (e) {
                await router.push('/')
            }
            isLoading.value = false
        }
    }

对于 Laravel,我们将创建以下内容:

  • 控制器 (,AuthController``TodoController)

  • 型号 (,Todo``User)

  • 路线 (api)

  • 中间件 ()。auth:sanctum

我们的路由在 中,它处理 Vue 使用的所有端点。api.php

Route::post('/auth/register', [AuthController::class, 'register']);
Route::post('/auth/login', [AuthController::class, 'login']);

Route::apiResource('todos', TodoController::class)->middleware('auth:sanctum');

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

中的寄存器逻辑注册用户并创建令牌。AuthController

public function register(Request $request): \Illuminate\Http\JsonResponse
{
    try {
        //Validated
        $validateUser = Validator::make($request->all(),
        [
            'name' => 'required',
            'email' => 'required|email|unique:users,email',
            'password' => 'required'
        ]);

        if($validateUser->fails()){
            return response()->json([
                'status' => false,
                'message' => 'validation error',
                'errors' => $validateUser->errors()
            ], 401);
        }

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password)
        ], 201);

        return response()->json([
            'status' => true,
            'message' => 'User Created Successfully',
            'token' => $user->createToken("API_TOKEN")->plainTextToken
        ], 200);

    } catch (\Throwable $e) {
        return response()->json([
            'status' => false,
            'message' => $e->getMessage()
        ], 500);
    }
}

登录名验证请求,检查用户是否存在,然后创建令牌:

public function login(Request $request): \Illuminate\Http\JsonResponse
{
    try {
        //Validated
        $validateUser = Validator::make($request->all(),
            [
                'email' => 'required',
                'password' => 'required'
            ]);
​
        if($validateUser->fails()){
            return response()->json([
                'status' => false,
                'message' => 'validation error',
                'errors' => $validateUser->errors()
            ], 401);
        }
​
        if(!Auth::attempt($request->only(['email', 'password']))){
            return response()->json([
                'status' => false,
                'message' => 'Email & Password does not exist.',
            ], 401);
        }
​
        $user = User::where('email', $request->email)->first();
​
        return response()->json([
            'status' => true,
            'message' => 'Logged In Successfully',
            'token' => $user->createToken("API_TOKEN")->plainTextToken
        ], 200);
​
    } catch (\Throwable $e) {
        return response()->json([
            'status' => false,
            'message' => $e->getMessage()
        ], 500);
    }
}

用于添加新待办事项的 POST 端点由待办事项控制器 — 中的方法管理。store``/api/todos

public function store(Request $request): \Illuminate\Http\JsonResponse
{
    $data = Todo::where('user_id', $request->user()->id)->where('title', $request->title);
    if ($data->first()) {
        return response()->json(['status' => false, 'message' => 'Already exist']);
    }
    $req = $request->all();
    $req['user_id'] = $request->user()->id;
    $data = Todo::create($req);
    return response()->json(['status' => true, 'data' => $data], 201);
}

这将管理更新端点,该端点在用户完成待办事项后调用,位于 。Todo``/api/todos/id

public function update(Request $request, $id): \Illuminate\Http\JsonResponse
{
    $validateUser = Validator::make($request->all(),
        [
            'has_completed' => 'required',
        ]);

    if ($validateUser->fails()) {
        return response()->json([
            'status' => false,
            'message' => 'validation error',
            'errors' => $validateUser->errors()
        ], 401);
    }

    $data = Todo::find($id);
    $data->has_completed = $request->has_completed;
    $data->update();
    return response()->json(['status' => true, 'data' => $data], 202);
}

当用户删除 时,将调用终结点,这将处理它:Todo``/api/todos/id

public function destroy(int $id): \Illuminate\Http\JsonResponse
{
    throw_if(!$id, 'todo Id is missing');
    Todo::findOrFail($id)->delete();
    return response()->json(['status' => true, 'message' => 'todo deleted']);
}

干得好,我们使用Laravel和Vue创建了一个单页应用程序!以下是我们的待办事项应用程序的最终结果和干得好,我们使用Laravel和Vue创建了一个单页应用程序!:

结论

与传统的 PHP/Vue 组合相比,我们能够确定使用 Vue 和 Laravel 创建简单的用户身份验证和待办事项应用程序要简单得多——这可能需要更多的设置工作。

在我看来,Vue 与 Laravel 的结合使得开发单页应用程序变得容易,因为无需担心路由、中间件或处理 CORS。在下面的评论部分让我知道您开发 SPA 的经验。

猜你喜欢

转载自blog.csdn.net/weixin_47967031/article/details/132590611