12/08/2018, 18:00

Viết blog bằng Markdown sử dụng Laravel và VueJS

Xin chào các bạn quay trở lại với blog của mình, hôm nay mình sẽ hướng dẫn các bạn cách tạo ứng dụng viết blog bằng Markdown sử dụng Laravel và VueJS Mình mặc định là các bạn đã tạo sẵn project bằng Laravel và đã chạy npm install nhé. Tiếp theo các bạn vào resources/assets/js/components tạo ...

Xin chào các bạn quay trở lại với blog của mình, hôm nay mình sẽ hướng dẫn các bạn cách tạo ứng dụng viết blog bằng Markdown sử dụng Laravel và VueJS

Mình mặc định là các bạn đã tạo sẵn project bằng Laravel và đã chạy npm install nhé.

Tiếp theo các bạn vào resources/assets/js/components tạo mới file Blog.vue. Sau đó các bạn vào app.js khai báo component Blog như sau:

Vue.component('blog', require('./components/Blog.vue'));

Tiếp theo chúng ta đưa component này vào trang welcome.blade.php, khi đó nội dung trang sẽ như sau:

<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <title>Laravel</title>
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <link rel="stylesheet" type="text/css" href="/css/app.css">
        <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
    </head>
    <body>
        <div id="app">
            <Blog></Blog>
        </div>
        <script src="/js/app.js"></script>
    </body>
</html>

Ổn rồi đó, các bạn nhớ chạy 2 command sau để khởi động app nhé:

php artisan serve
npm run watch

Tiếp theo để viết được Markdown ta sử dụng package vue-markdown, cài đặt như sau:

npm install --save vue-markdown

Sau đó ta quay lại file Blog.vue và sửa như sau:

<template>
    <div class="my-blog container full-height">
        <div class="row editor-toolbar">
            <div class="col-md-12">
                <a class="fa fa-bold"></a>
                <a title="Italic (Cmd-I)" class="fa fa-italic"></a>
                <a title="Strikethrough" class="fa fa-strikethrough"></a>
                <a title="Big Heading" class="fa fa-header fa-header-x fa-header-1 active"></a>
                <a title="Medium Heading" class="fa fa-header fa-header-x fa-header-2"></a>
                <a title="Small Heading" class="fa fa-header fa-header-x fa-header-3"></a>
                <i class="separator">|</i>
                <a title="Code (Cmd-⌥-C)" class="fa fa-code"></a>
                <a title="Quote (Cmd-')" class="fa fa-quote-left"></a>
                <a title="Generic List (Cmd-L)" class="fa fa-list-ul"></a>
                <a title="Numbered List (Cmd-⌥-L)" class="fa fa-list-ol"></a>
                <a title="Insert Table" class="fa fa-table"></a>
                <a title="Insert Horizontal Line" class="fa fa-minus"></a>
                <a title="Clean block (Cmd-E)" class="fa fa-eraser fa-clean-block"></a>
                <i class="separator">|</i>
                <a title="Insert Link" class="fa fa-link"></a>
                <a title="Insert Image" class="fa fa-image"></a>
                <i class="separator">|</i>
                <a title="Toggle Preview" class="fa fa-eye no-disable"></a>
                <a title="Side by side mode" class="fa fa-columns no-disable no-mobile"></a>
                <a title="Fullscreen mode" class="fa fa-arrows-alt no-disable no-mobile"></a>
                <i class="separator">|</i>
                <a title="Undo" class="fa fa-undo no-disable"></a>
                <a title="Redo" class="fa fa-repeat no-disable"></a>
                <i class="separator">|</i>
                <a title="Markdown Help" class="fa fa-question-circle"></a>
            </div>
        </div>
        <div class="row full-height markdown">
            <div class="col-md-6 area-input full-height">
                <div class="form-group">
                    <textarea :value="input" @input="update" class="syncscroll form-control full-height" name="myscroll1"></textarea>
                </div>
            </div>
            <div class="col-md-6 preview full-height syncscroll" name="myscroll1">
                <VueMarkdown :source="input"></VueMarkdown>
            </div>
        </div>
    </div>
</template>

<script>
    import VueMarkdown from 'vue-markdown'
    export default {
        components: {
            VueMarkdown
        },
        data() {
            return {
                input: '
            }
        },
        computed: {
            compiledMarkdown() {
                return this.input
            }
        },
        methods: {
            update: _.debounce(function (e) {
              this.input = e.target.value
            }, 300)
        }
    }
</script>

<style lang="scss" scoped>
.separator {
    display: inline-block;
    awidth: 0;
    border-left: 1px solid #d9d9d9;
    border-right: 1px solid #fff;
    color: transparent;
    text-indent: -10px;
    margin: 0 6px;
}
.preview {
    background: white;
    border-radius: 5px;
    border: solid 1px #ddd;
    height: 90vh;
    overflow: auto;
}
.area-input {
    textarea {
        height: 90vh;
    }
}
.editor-toolbar {
    a {
        display: inline-block;
        text-align: center;
        text-decoration: none!important;
        color: #2c3e50!important;
        awidth: 30px;
        height: 30px;
        margin: 0;
        border: 1px solid transparent;
        border-radius: 3px;
        cursor: pointer;
        line-height: 30px;
        &:hover {
            background: #fcfcfc;
            border-color: #95a5a6;
        }
    }
}
.markdown {
    margin-top: 20px;
}
</style>

Giải thích về code bên trên:

  • Đầu tiên ta cần import VueMarkdown vào để sử dụng
  • Mình tạo ra 2 phần 1 bên là textarea và phần preview kết quả
  • Thanh editor bar ở phía trên mình làm giống như của Viblo
  • Các bạn chú ý ở textarea mình sử dụng @input mục đích là mỗi khi người dùng nhập vào mình sẽ xử lý input để không render ngay, vì việc này làm liên tục sẽ dẫn tới việc app của chúng ta bị chậm, nên mình sử dụng cách là mỗi khi có input ta gọi đến hàm update, hàm này sử dụng hàm debounce của lodash (mặc định lodash được cài cùng app ban đầu luôn, nếu chưa có các bạn cài bằng npm nhé)
  • Để xuất kết quả bằng markdown ta chỉ cần bind source của component VueMarkdown là được
  • Phần CSS bên dưới cho đẹp tí thôi             </div>
            
            <div class=
0